单点优化系列--session共享踩坑记


声明:本文转载自https://my.oschina.net/u/2342969/blog/1512185,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

本文将继续单机架构升级集群系列博文, 记录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)

注意事项

  1.     spring session依赖spring mvc
  2.     本文基于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)

 

本文发表于2017年08月17日 18:36
(c)注:本文转载自https://my.oschina.net/u/2342969/blog/1512185,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2215 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1