本文将继续单机架构升级集群系列博文, 记录tomcat的Session共享工作的记录,希望能帮助到需要的朋友们,也欢迎大牛们批评指正!!       
   背景
          在博文( https://my.oschina.net/u/2342969/blog/995598 )中描述了本次架构升级的背景
          在博文( https://my.oschina.net/u/2342969/blog/1036702 )中记录了redis主从集群搭建 
          在博文(https://my.oschina.net/u/2342969/blog/1162829 )中记录了 nginx+keepalived主从集群高可用搭建工作 
          以上博文有需要的朋友们可以观阅
  本文将记录 tomcat的Session共享工作, 在此参考了 spring官网文档(
  http://docs.spring.io/spring-session/docs/1.3.1.RELEASE/reference/html5/#httpsession-redis-xml
  )
   概述
      基于以前博文可以了解到,现阶段,我们是一台nginx、2台tomcat       
      在我们未做session共享的情况下,使用的nginx的iphash策略,以致停掉一个tomcat_A,所有请求都会集中到另外一台tomcat_B 并需要重新登陆, tomcat_A启动后,已经请求道tomcat_B的请求量还是不会减少,
      做了session共享的应用,是基于 tomcat sessionmanager 做的,依赖tomcat版本,需要修改服务器配置,以致如需换应用服务器还需要重新搭建
      现优化方案,基于spring session,将session共享做到应用层,净化服务器配置,便于应用迁移。
   环境准备
     -     centOs:6.5
-     nginx : 1.5.8 
-     redis:版本任意(本文使用的2.8) 
注意事项
     -     spring session依赖spring mvc
-     本文基于redis实现session共享
基于lettuce完成
       pom依赖
      在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>  
       session共享spring配置
      在项目增加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" />
     修改web.xml
      加载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>  
   基于jedis完成
       pom依赖
      在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>  
       session共享spring配置
      在项目增加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>
    
   修改web.xml
      加载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)