本文將繼續單機架構升級集羣系列博文, 記錄tomcat的Session共享工做的記錄,但願能幫助到須要的朋友們,也歡迎大牛們批評指正!! html
在博文( https://my.oschina.net/u/2342969/blog/995598 )中描述了本次架構升級的背景html5
在博文( https://my.oschina.net/u/2342969/blog/1036702 )中記錄了redis主從集羣搭建 java
在博文(https://my.oschina.net/u/2342969/blog/1162829 )中記錄了 nginx+keepalived主從集羣高可用搭建工做 nginx
以上博文有須要的朋友們能夠觀閱web
本文將記錄 tomcat的Session共享工做, 在此參考了 spring官網文檔(redis
http://docs.spring.io/spring-session/docs/1.3.1.RELEASE/reference/html5/#httpsession-redis-xmlspring
)express
基於之前博文能夠了解到,現階段,咱們是一臺nginx、2臺tomcat apache
在咱們未作session共享的狀況下,使用的nginx的iphash策略,以至停掉一個tomcat_A,全部請求都會集中到另一臺tomcat_B 並須要從新登錄, tomcat_A啓動後,已經請求道tomcat_B的請求量仍是不會減小,tomcat
作了session共享的應用,是基於 tomcat sessionmanager 作的,依賴tomcat版本,須要修改服務器配置,以至如需換應用服務器還須要從新搭建
現優化方案,基於spring session,將session共享作到應用層,淨化服務器配置,便於應用遷移。
nginx : 1.5.8
redis:版本任意(本文使用的2.8)
在maven項目pom.xml 增長如下依賴
<dependencies> <!-- ... --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.1.RELEASE</version> <type>pom</type> </dependency> <dependency> <groupId>biz.paluch.redis</groupId> <artifactId>lettuce</artifactId> <version>3.5.0.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.4.RELEASE</version> </dependency> </dependencies>
在項目增長spring-session.xml
目錄以下: src/main/webapp/WEB-INF/spring/spring-session.xml
<context:annotation-config/> <!--開啓註解配置--> <!--springSession 配置--> <bean id="sessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <!--修改session的有效時間 以秒爲單位,默認值是1800,便於測試修改成60秒--> <property name="maxInactiveIntervalInSeconds" value="60"></property> </bean> <!-- 如不使用P命名空間或者屬性注入 默認redis ip爲localhost,port爲6379 --> <bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:hostName="127.0.0.1" p:port="6379" p:password="your-password" p:timeout="3000" p:database="3" />
加載spring-session.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/*.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
在web.xml增長攔截器, 此攔截器必定要加載全部攔截器前面,DispatcherServlet啓動以後
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
在maven項目pom.xml 增長如下依賴
<dependencies> <!-- ... --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.3.1.RELEASE</version> <type>pom</type> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.5.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.4.RELEASE</version> </dependency> </dependencies>
在項目增長spring-session.xml
目錄以下: src/main/webapp/WEB-INF/spring/spring-session.xml
<context:annotation-config/> <!--開啓註解配置--> <!--springSession 配置--> <bean id="sessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> <!--修改session的有效時間 以秒爲單位,默認值是1800,便於測試修改成60秒--> <property name="maxInactiveIntervalInSeconds" value="60"></property> </bean> <!-- 如不使用P命名空間或者屬性注入 默認redis ip爲localhost,port爲6379 --> <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName" value="10.34.59.12"></property> <property name="port" value="6379"></property> <property name="database" value="3"></property> </bean>
此處深坑,若是一個項目配置了多個JedisConnectionFactory,下圖bean沒法找到惟一的JedisConnectionFactory
查看RedisHttpSessionConfiguration源碼可使用RedisTemplate關於session的redis,根據下圖能夠更改JedisConnectionFactory配置以下:
<bean id="sessionRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory"> <bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:database="3" p:host-name="${redis.host}" p:port="${redis.port}" p:usePool="true" p:password="${redis.pass}" p:pool-config-ref="jedisPoolConfig1" p:timeout="${redis.timeout}"> </bean> </property> </bean>
加載spring-session.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/*.xml </param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
在web.xml增長攔截器, 此攔截器必定要加載全部攔截器前面,DispatcherServlet啓動以後
<filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
到此應用就具有了session共享的功能, 有不當或者錯誤的地方,歡迎拍磚!!!
關於配置了多個JedisConnectionFactory, RedisHttpSessionConfiguration 報錯的解決方案,煩請大神不吝賜教,報錯以下:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisMessageListenerContainer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.data.redis.connection.RedisConnectionFactory]: : No qualifying bean of type [org.springframework.data.redis.connection.RedisConnectionFactory] is defined: expected single matching bean but found 2: jedisFactory,jedisFactory1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.data.redis.connection.RedisConnectionFactory] is defined: expected single matching bean but found 2: jedisFactory,jedisFactory1 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652) at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1863) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618) at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801) at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487) at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97) at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328) at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420) at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322) at sun.rmi.transport.Transport$2.run(Transport.java:202) at sun.rmi.transport.Transport$2.run(Transport.java:199) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Transport.java:198) at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.data.redis.connection.RedisConnectionFactory] is defined: expected single matching bean but found 2: jedisFactory,jedisFactory1 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739)