记一次奇怪的cookie丢失问题
记一次奇怪的cookie丢失问题
事情的经过
今天早上,前端小伙子报出异常,一个简单的vue页面,在打开菜单的时候,有时候会出现登录异常.
经过日志排查,逻辑上看,好像是cookie没传.
cookie没传??怎么可能,cookie机制可是浏览器提供的.想不传都难好吧.....
调试,跟踪,确实是在request里面没有cookie...在request里面,有请求,有数据,就是没有cookie...
然后就加代理,看网络请求,发现前端没骗人,cookie是妥妥的带在了header里...
在看应用,确实没有啊,难道是那浓眉大眼的tomcat有bug了???
几次跟踪下来,只要打了断点,就没有发现cookie丢失的问题...搞到中午,基本怀疑是tomcat的事情了,而且是在多线程下,有可能和同一个浏览器打开的channel有关....(天真的不行)
中午无眠啊.....
到了晚上,急匆匆开完会回来,调试小伙子要求我请吃饭....这个要看的...
调试小伙子把断点打在一个很奇怪的位置上,见下图....

看来tomcat的代码也就那样啊,怎么会有那样的代码呢???
但是,奇怪的问题出现了,居然在同一个jvm里面,在不同的请求处理逻辑上,出现一个id相同的request....
一个request的位置出现在请求的filter里面,一个request的位置出现在请求处理后的对外调用上.....对外调用是上一个请求结束后的一个日志信息回调,中间需要获得当前请求中的部分内容.
一个简单的实例图如下

为了在回调中拿到之前请求的request,在代码里面对原有的request进行了缓存,放在了InheritableThreadLocal中.
原来不是tomcat不靠谱,是InheritableThreadLocal导致的问题....去掉InheritableThreadLocal,问题就解决了.
问题分析
那么问题来了,到底是什么原因导致了在多线程的环境下,cookie就丢失了呢??
很抱歉,问题解决了,但是多线程还是偶发性问题,原因只能靠猜.
- 第一个请求在处理的时候,在InheritableThreadLocal中缓存了request对象
- 在请求完成后,第二个请求发起,而回调处理也在进行
- 当第二请求被处理前,已经需要对request进行缓存,而InheritableThreadLocal的继承操作,直接是对象赋值.
- 从而导致在另外一个线程的InheritableThreadLocal的request对象,也一起被修改
- 这个时候,再看tomcat对cookie的处理,如果一个线程执行到3167行,另外一个线程执行到3174行,就导致了2个线程修改了同一个request对象的同一个属性
这种不安全的多线程操作,非常隐蔽.和InheritableThreadLocal类似的还有阿里的TTL框架,特别在缓存对象的时候,需要慎重.
