单点优化系列--session共享踩坑记
博客专区 > java_龙 的博客 > 博客详情
单点优化系列--session共享踩坑记
java_龙 发表于4个月前
单点优化系列--session共享踩坑记
  • 发表于 4个月前
  • 阅读 901
  • 收藏 80
  • 点赞 0
  • 评论 2

腾讯云 新注册用户 域名抢购1元起>>>   

摘要: 由于公司一直使用nginx的iphash负载策略,iphash的优劣这里不做分析,在大量请求下,iphash策略是个坑, 抛开此策略,多个服务器做负载均衡就要先做session共享

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

 

共有 人打赏支持
粉丝 40
博文 15
码字总数 22942
评论 (2)
ixiaohei
我记得我当时做是指定一个名字就好了,当时还看过spring session源代码,现在忘记了,不过可以搞个内部bean配置redis可以解决。后面我们用户量太多,集群太大已经废弃使用session这种方案。都是sso单点拿token。然后filter过滤拿到token去sso服务端解析到用户数据。
java_龙

引用来自“ixiaohei”的评论

我记得我当时做是指定一个名字就好了,当时还看过spring session源代码,现在忘记了,不过可以搞个内部bean配置redis可以解决。后面我们用户量太多,集群太大已经废弃使用session这种方案。都是sso单点拿token。然后filter过滤拿到token去sso服务端解析到用户数据。
:+1: 3Q
×
java_龙
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: