redisson-tomcat会话共享之session失效BUG排查

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redisson-tomcat会话共享之session失效BUG排查相关的知识,希望对你有一定的参考价值。

参考技术A 使用redisson-tomcat很简单,只需要两个步骤:

在tomcat/conf/context.xml增加配置:

(rv表示redisson的版本号,tv表示tomcat的版本号)

参考 https://github.com/redisson/redisson/tree/master/redisson-tomcat

运维伙伴配置好负载均衡后(集群环境,一台一台发布,发布前剔除负载,发布后加入负载),我便开始测试。

一开始正常,但多次调试后,偶尔会出现session失效的问题,再次多次尝试后找到了session失效的触发条件:
第一次切换负载时,能正常访问,但第二次切换负载时,session会失效。

由于tomcat上层有slb做负载均衡、nginx做反向代理,首先得排查是不是它们引起的问题。
但这很难,于是我换了个思路,绕过上层直接访问tomcat,查看是否有问题。
排查流程:

tomcat返回Set-Cookie响应头,说明session已经失效,并重新创建了一个新的session。
所以,问题不是出在slb和nginx上。

考虑一番,打算直接从数据上查看是否有异样,重复上述步骤:

查看redis,一切正常:

查看redis,发现问题:

什么???
session的isValid变成false了,意味着session在第一次切负载的时候就已经失效了!

响应头依然有Set-Cookie,表示session的确失效了。
此时基本上确定问题是出在redisson-tomcat了。

在第一次getSession的时候,会调用sessionManager的createSession方法。
在切换负载的时候,会携带session id去访问另外一台tomcat,调用sessionManager的findSession方法:
乍眼一看,没有问题呀。

关键在于session.setId,调用了sessionManager的add(session)方法:
但是,到这一步也没问题。

问题在于,RedissonTomcat重写了sessionManager的add方法:
它调用了RedissonSession的自定义方法save

Redisson的save方法将所有字段同步到redis:

回过头看findSession,在还没有loadAttr的时候,就调用了setId方法,将一大堆还没有初始化好的值同步到了redis,导致session的isValid被置为false:

查看redisson的release记录,在最新版本已经修复了:

我们看一下,redisson是怎么修复的,仅仅是交换了setId和load(attrs)的顺序:

redisson 3.x版本最低要求jdk1.8,然而我们项目用的是jdk1.7。
于是我使用updateMode=AFTER_REQUEST模式暂时解决了这个问题。
AFTER_REQUEST原理是在tomcat容器的pipeline增加了一个Valve:

UpdateValve在请求结束后,同步所有字段到redis:

javaweb之HttpSession对象

1.session会话追踪原理 客户端第一次请求服务端,服务端会创建一个session对象并且存储下来,之后会将session的唯一标识sessionId设置到响应头中传给客户端 客户端之后请求就会在cookie中携带第一次请求后服务端传过来的sessionId,服务端能通过客户端传过来的sessi

以上是关于redisson-tomcat会话共享之session失效BUG排查的主要内容,如果未能解决你的问题,请参考以下文章

PHP会话控制之如何正确设置session_name

javaweb之HttpSession对象

web存储机制(localStorage和sessionStorage)

8Python全栈之路系列之Django Cookie 与Sessi

java之Cookie和Session

nginx六 之Session共享