会话是识别用户,跟踪用户访问行为的一个手段,经过cookie(存在客户端)或session(存在服务端)来判断本次请求是那个客户端发送过来;经常使用的会话保持有绑定会话,就是前边咱们聊的在代理上经过算法或经过给客户端响应首部加cookie这种方式来保持同一cookie或同一ip地址的请求始终发送到同一后端server进行响应;可是这样的会话绑定的方式存在一个问题,就是当后端某一server宕机,那么以前上面的全部会话信息将消失,那么后续的客户端来请求,代理是否要把请求调度到后端宕机的server呢?若是说调度上去呢,那么用户以前的会话信息又没有了,若是说不调度呢,那么用户将不可以获得服务;因此对于这种状况咱们须要把会话都同步到后端全部server上,即使某一台或几台后端server宕机了,不会致使用户的会话信息丢失,一样服务也是可用的;这种冗余的方式保存会话信息,使得用户的会话信息可以在任何一台后端server上都会有;这也意味着只要有用户来请求,前端调度器能够任意把请求调度到后端的某一台server上,而后服务端把本次请求的用户会话信息经过广播的方式,通知给其余后端server,这样一来这个客户端后续来请求,无论调度到后端那一台server上,由于后端server上都有这个客户端以前请求的会话信息,因此无论到那一台都可以识别;对于tomcat来说,它内部就有一个组件支持这样的功能,它能够基于多播通讯的方式,把会话信息同步给后端其余节点,这个组件就是cluster;前端
示例:使用tomcat cluster组件来定义tomcat的会话复制集群nginx
环境说明web
名称 | ip地址 | 端口 |
代理Nginx | 192.168.0.41 | 80 |
应用服务tomcatA | 192.168.0.42 | 8080 |
应用服务tomcatB | 192.168.0.43 | 8080 |
准备测试页面,以及配置tomcatA算法
提示:以上是myapp里的内容以及文件目录结构apache
提示:以上配置表示部署一个/myapp的应用,它的文件路径在/webapps/myapp,而且在engine上配置了 jvmRoute=“tomcatA”;后端
提示:cluster配置须要注意上面打红框的位置,在官方配置文档中,后面的<ClusterListener 的后面没有把标签闭合了,咱们在使用时须要给它闭合了,不然会出现语法错,致使tomcat起不来;其次就是咱们须要更改接收器的ip地址,默认它是auto,auto表示自动监听本机一个地址,这个地址也多是127.0.0.1,若是监听在127.0.0.1,那么主机就不可以接收到,其余节点发来的会话信息;说下这个配置文件吧,cluster组件中主要就是定义了DeltaManager的属性,该组件用于处理增量会话的事务,也就是用这个管理的功能实现多节点复制会话信息;其次咱们要在其cluster内部定义个chanel,这个组件主要定义集群通讯和各成员的一些属性,好比成员关系断定呀,接收器和发送器;Membership组件用于定义成员关系断定的,里面主要定义多播地址和端口等属性,若是多播地址相同,那么就是同一集群的成员,不然不是;Receiver主要用于定义接收器的相关属性,好比接收器监听的地址和端口超时时长,最大线程等等;Sender用于指定发送器,发送器咱们这里不须要认为手动定义,用官方给定的示例便可;后面的Interceptor主要定义了tcp报文的检查以及消息摘要,后面两台哦Interceptor主要做用是保证tcp报文的完整和正确性;Deployer主要用于定义部署应用相关属性,它的主要做用是若是咱们定义了集群,咱们能够在集群成员中的一台server上部署好应用,而后其余成员能够经过网络自动部署;一般咱们建议使用这个自动部署的功能;tomcat
完整的server.xml配置服务器
<?xml version='1.0' encoding='utf-8'?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JasperListener" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> <Context path="/myapp" docBase="/webapps/myapp" reloadable=""/> <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="192.168.0.42" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> </Host> </Engine> </Service> </Server>
给咱们定义的应用修改器web.xml 在其中加上<distributable/>元素cookie
提示:对于web.xml配置文件,咱们能够从/etc/tomcat/中复制一份到本身的应用目录结构里,而后在非注释掉位置加上<distributable/>元素便可网络
对于tomcatB来讲,咱们也须要准备好一样的文件,为了区分,咱们把index.jsp修改为tomcatB ,在配置文件中咱们须要修改接收器的监听地址,以及jvmRoute的值,其余的均可以不变
到此tomcat会话复制集群就配置好了;其实从上面的配置文件能够看大,tomcat的会话复制集群就是利用多播地址通讯,一个请求无论到集群那个基点,它都会经过多播通讯,把会话信息以组播的方式发送给其余成员;这里建议把接收器的地址专门用张网卡配置好地址;接下来咱们启动下tomcatA,tomcatB,而后看看日志是否初始集群成功,并接收到集群成员接收器的地址;
提示:这里注意一点若是tomcat启动特别慢,就是8005端口要等好久才起来,能够尝试安装rng-tools,并启动rngd,这样能够加快tomcat启动
提示:若是在tomcatA的日志中可以看到tomcatB的接收器地址和端口,那么就表示tomcatA已经识别到tomcatB,并把tomcatB看成集群成员加入到集群;一样在tomcatB的日志中可以看到tomcatA的接收器地址和端口,表示tomcatB已经识别tomcatA并把它加入到集群;
配置nginx负载均衡后端两台tomcat server
提示:这里须要主要反代时须要把反代的URI和后面proxy_pass后面的URI相同,不然代理后,会话复制集群不会生效;
验证:检查nginx的配置文件语法,启动nginx访问192.168.0.41/myapp看看有什么变化
提示:能够看到访问192.168.0.41/myapp时sessionid始终没有发生变化,变化的只有后面的jvmRoute的值和页面的值;这说明咱们访问nginx时,nginx也基于本身的轮询算法把请求调度到后端去了,第一次访问时,后端server会响应一个set-cookie的首部,把当前访问的页面的session信息响应给客户端,第二次访问客户端就会把上一次访问相应的cookie带上去访问,这时后端server接受到客户端发送过来的cookie,而后就在本身内存里找对应的session信息;因为后端server是把session信息基于多播通讯的方式共享给集群其余节点,因此第二次无论调度到那台server上,对应server都会有该客户端第一次访问服务端的session信息;因此咱们第二次访问时,sessionid仍是第一次访问服务器的sessionid,后面的tomcatB表示由tomcatB这个jvmRoute处理的此次请求;