生产环境我们的应用示例不可能是单节点部署, 通常都是多结点部署, 结点上层会进行域映射, 实例之间负载响应请求. 比如常见的Nginx + Tomcat负载均衡场景中。常用的均衡算法有IP_Hash、轮训、根据权重、随机等。不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题。
解决方案
IP_HASH
nginx可以根据客户端IP进行负载均衡,在upstream里设置ip_hash,就可以针对同一个C类地址段中的客户端选择同一个后端服务器,除非那个后端服务器宕了才会换一个. 这样如果该类QPS高会导致该台服务器的负载升高,负载不均.
通过容器插件
在容器层面扩展可共享存储的插件; 比如基于Tomcat的tomcat-redis-session-manager,基于Jetty的jetty-session-redis等等。好处是对项目来说是透明的,无需改动代码。该方案由于过于依赖容器,一旦容器升级或者更换意味着又得从新来过。并且代码不在项目中,对开发者来说维护也是个问题。
会话管理工具
自己写一套会话管理的工具类,包括Session管理和Cookie管理,在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中。很显然这个方案灵活性最大,但开发需要一些额外的时间。并且系统中存在两套Session方案,很容易弄错而导致取不到数据。
开源解决方案
这里以开源框架Spring-Session为例,Spring-Session扩展了Servlet的会话管理(所有的request都会经过SessionRepositoryFilter,而 SessionRepositoryFilter是一个优先级最高的javax.servlet.Filter,它使用了一个SessionRepositoryRequestWrapper类接管了Http Session的创建和管理工作),既不依赖容器,又不需要改动代码. 可插拔, 轻量级. 支持多维度存储;诸如 Redis 、Pivotal GemFire、Jdbc、Mongo 、Hazelcast等
SpringSession应用
SpringMvc项目使用SpringSession
maven依赖
1 | <dependency> |
配置web.xml
1 | <filter> |
配置redis、以及redisHttpSession存储
1 | <beans xmlns="http://www.springframework.org/schema/beans" |
测试代码
1 |
|
测试输出
启两个容器实例,端口分别使用8080和8081进行访问
实例1访问结果
实例2访问结果
查看redis中存储的session
应用SpringSession
maven依赖
1 | <dependencies> |
启动主类增加注解
启动类上增加注解@EnableRedisHttpSession
1 |
|
应用配置文件配置
应用配置文件application.properties
增加如下配置
1 | # spring redis |