Tomcat Request Cookie 丢失问题
优质博文IT-BLOG-CN一、问题描述生产环境偶尔(涉及到多线程处理)出现前端传递Cookie为空的告警导致前端请求丢失出现请求失败问题。告警内容如下前端传递Cookie为空 告警内容服务端获取request Cookie为空请尽快处理 AppIdxxxxxx ipxx.xx.xxx.xx 告警事件2024-03-15背景为什么要加Cookie告警项目出海需要保证多语言语言信息从Cookie中获取所以添加了Cookie告警告警后发到工作群中但是相关开发人员告知自己能够正常访问没有问题因为正好周五自己觉得偶发性肯定和并发相关所以周末研究了下代码发现和Tomcat Rquest复用机制和ThreadLocal的使用存在缺陷导致这个偶发性问题在分析原因前先需要搞懂一个概念request在tomcat里面是循环使用的二、Tomcat 中 Reqeust 复用机制Request对象的复用机制是为了提高性能和减少垃圾收集压力而设计的。Tomcat使用了一种对象池的机制来管理Request对象和Response对象。通过复用这些对象Tomcat可以避免频繁地创建和销毁对象从而提高系统的效率。复用机制的工作原理【1】对象池Tomcat维护一个对象池用于存储Request对象和Response对象。当一个新的HTTP请求到达时Tomcat从对象池中获取一个空闲的Request对象和Response对象。如果对象池中没有空闲的对象Tomcat会创建新的对象。简单看个案例public class RequestPool { private StackRequest pool new Stack(); // 获取对象getRequest 方法从对象池中获取一个 Request 对象。如果对象池为空则创建一个新的 Request 对象。 public Request getRequest() { if (pool.isEmpty()) { return new Request(); } else { return pool.pop(); } } // 释放对象releaseRequest 方法将 Request 对象重置调用 recycle 方法并放回对象池中。 public void releaseRequest(Request request) { request.recycle(); pool.push(request); } }【2】对象重置当一个请求处理完毕后Request对象会被重置通过调用recycle方法以清除上一次请求的状态使其可以安全地用于下一个请求。以下是org.apache.catalina.connector.Request类中recycle方法的简化源码和解释public class Request { // Various fields representing the state of the request private String protocol; private String method; private String requestURI; private String queryString; private String remoteAddr; private String remoteHost; private String serverName; private int serverPort; private boolean secure; private InputStream inputStream; private Reader reader; private ServletInputStream servletInputStream; private BufferedReader bufferedReader; private MapString, Object attributes; private MapString, String[] parameters; private Cookie[] cookies; private HttpSession session; // Other fields and methods... /** * Recycle this request object. */ public void recycle() { // Reset the state of the request object // 重置基本属性recycle 方法将 Request 对象的基本属性如 protocol、method、requestURI 等重置为初始状态通常为 null 或默认值。 // 清空集合和数组attributes 和 parameters 集合被清空以确保没有残留的请求数据。cookies 数组也被重置为 null。 // 重置流和读者inputStream、reader、servletInputStream 和 bufferedReader 被重置为 null以确保没有残留的输入流和读者对象。 // 重置会话session 被重置为 null以确保没有残留的会话信息。 protocol null; method null; requestURI null; queryString null; remoteAddr null; remoteHost null; serverName null; serverPort 0; secure false; inputStream null; reader null; servletInputStream null; bufferedReader null; attributes.clear(); parameters.clear(); cookies null; session null; // Other reset logic... } }recycle执行的时机recycle方法在Tomcat源码中的调用时机主要是在请求处理完毕之后Request对象被返回到对象池之前。具体来说recycle方法通常在以下几个场景中被调用【1】请求处理完毕后在Tomcat的org.apache.coyote.Request类中recycle方法通常在请求处理完毕后被调用。例如在AbstractProcessorLight类中处理请求和响应的逻辑中recycle方法被调用来重置Request对象。// org.apache.coyote.AbstractProcessorLight public class AbstractProcessorLightS implements Processor { // Various fields and methods... Override public SocketState process(SocketWrapperBaseS socketWrapper, SocketEvent status) throws IOException { // Process the request and response try { // Request processing logic... } finally { // Recycle the request and response objects request.recycle(); response.recycle(); } return SocketState.CLOSED; } }【2】连接关闭时在Tomcat的org.apache.coyote.http11.Http11Processor类中当连接关闭时recycle方法也会被调用。例如当处理完一个请求并决定关闭连接时会调用recycle方法。// org.apache.coyote.http11.Http11Processor public class Http11Processor extends AbstractProcessorLightSocketChannel { // Various fields and methods... Override public SocketState service(SocketWrapperBaseSocketChannel socketWrapper) throws IOException { // Service the request and response try { // Request servicing logic... } finally { // Recycle the request and response objects request.recycle(); response.recycle(); } return SocketState.CLOSED; } }【3】异常处理在处理请求的过程中如果发生异常Tomcat也会确保调用recycle方法来重置Request对象。例如// org.apache.coyote.http11.Http11Processor public class Http11Processor extends AbstractProcessorLightSocketChannel { // Various fields and methods... Override public SocketState service(SocketWrapperBaseSocketChannel socketWrapper) throws IOException { try { // Request servicing logic... } catch (Exception e) { // Handle exception and recycle request request.recycle(); response.recycle(); throw e; } } }后期原因分析中需要使用到RequestFacade这里解释下RequestFacade与Request之间的关系RequestFacade是一个包装类Facade用于保护底层的Request对象确保应用程序无法直接访问和修改内部实现细节。【1】Request类Request类是Tomcat内部用来表示HTTP请求的类包含了请求的所有详细信息。该类提供了许多方法来访问和操作请求的各个部分例如请求头、请求参数、输入流等。【2】RequestFacade类RequestFacade类是一个包装器用于保护Request对象。它实现了javax.servlet.http.HttpServletRequest接口并将方法调用委托给内部的Request对象。通过使用RequestFacadeTomcat确保了应用程序只能通过标准的HttpServletRequest接口访问请求数据而不能直接访问或修改Request对象的内部实现。具体实现在Tomcat中RequestFacade类通常包含一个Request对象的引用并将所有的接口方法调用委托给这个内部的Request对象。例如// org.apache.catalina.connector.RequestFacade public class RequestFacade implements HttpServletRequest { private final Request request; public RequestFacade(Request request) { this.request request; } Override public String getParameter(String name) { return request.getParameter(name); } // Other methods from HttpServletRequest interface // All methods delegate to the internal Request object }使用场景在Tomcat处理请求的过程中当需要将HttpServletRequest对象传递给应用程序时Tomcat会创建一个RequestFacade实例并将内部的Request对象传递给它。例如// org.apache.catalina.connector.CoyoteAdapter public class CoyoteAdapter implements Adapter { Override public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception { Request request (Request) req.getNote(ADAPTER_NOTES); Response response (Response) res.getNote(ADAPTER_NOTES); // Create a RequestFacade to pass to the application HttpServletRequest requestFacade request.getRequest(); // Pass the RequestFacade to the application context.getPipeline().getFirst().invoke(requestFacade, response); } }ThreadLocal的原理不清楚可以参考ThreadLocal 类三、原因分析【1】第一次请求由线程A正常执行执行完成后执行recycle方法将RequestFacade中的属性修改为null准备下次复用但是当前线程的ThreadLocal没有被清理。【2】第二次请求恰好也由线程A执行这也是偶发的原因通过ThreadLocal获取RequestFacade对象并通过getCookies获取Cookie因为第一次请求结束后将Cookie置为null并将cookiesParsed修改为了false但是这次请求再次调用getCookies的时候将cookiesParsed修改为了true。用来表示RequestFacade A的Cookies已经被解析过了。同时需要注意此时第一次请求的生命周期已经结束了所以重置cookiesParsed的操作就不复存在了Tomcat重新复用RequestFacade A的时候Cookies就会获取到一个null。Override public Cookie[] getCookies() { if (!cookiesParsed) { parseCookies(); } return cookies; } protected void parseCookies() { cookiesParsed true; Cookies serverCookies coyoteRequest.getCookies(); int count serverCookies.getCookieCount(); if (count 0) { returnl } cookies new Cookie[count]; }【3】第三次请求时Tomcat复用了RequestFacade A当正常解析Cookies的时候发现cookiesParsed为true就跳过了正确解析的环节当需要使用Cookie的时候发现为空本次请求直接被中止。灵异事件解决方案【1】ThreadLocal使用完后一定需要clean【2】不要在跨线程中使用request对象。可以使用-Dorg.apache.catalina.connector.RECYCLE_FACADEStrue禁止复用。在项目的extraenv.sh中设置参数后如果有访问已经被回收的request对象就会抛出The request object has been recycled and is no longer associated with this facade异常以此就能定位到问题

相关新闻

用AI撰写专著不再愁!工具推荐与使用攻略大整合

用AI撰写专著不再愁!工具推荐与使用攻略大整合

创新与AI专著生成工具助力学术创作 创新是学术专著的核心,也是写作过程中最难达成的标准。一部优秀的专著不能仅仅是将之前的研究成果简单地拼凑在一起,它必须提出贯穿全书的独到见解、理论构架或研究方法。在纷繁复杂的学术资料中,找到尚未…

2026/5/17 4:41:30 阅读更多 →
基于公开数据增强的规划许可类钓鱼攻击机理与防御体系研究

基于公开数据增强的规划许可类钓鱼攻击机理与防御体系研究

摘要: 随着数字化转型的深入,针对特定垂直领域的定向网络钓鱼攻击呈现出高度专业化与精准化的趋势。2026年3月,美国互联网犯罪投诉中心(IC3)发布警示,披露了一类新型网络钓鱼攻击模式:攻击者通过…

2026/5/17 12:54:09 阅读更多 →
N卡环境下B站闪屏的原因与解决办法

N卡环境下B站闪屏的原因与解决办法

N 卡打开程序后 B 站闪屏,核心原因是GPU 硬件加速与显卡驱动 / 程序的兼容性冲突,尤其是全屏时渲染切换导致闪烁。以下是分场景的解决步骤:一、B 站客户端专属解决(最推荐)打开 B 站电脑客户端,点击右上角「…

2026/5/17 12:54:08 阅读更多 →

最新新闻

C语言 二维数组在内存中的存储

C语言 二维数组在内存中的存储

1.二维数组在内存中是怎么存储的?请问这个二维数组在内存中的布局?int arr[3][4] { {1,2,3,4,},{5,6,7,8},{9,10,11,12 } };你的答案是这样的吗。我们说这是我们想象的逻辑结构,那实际的布局,即物理结构是怎样的呢?in…

2026/7/5 15:00:27 阅读更多 →
手把手教你学Simulink——基于平均电流模式(Average Current Mode Control, ACMC)的双向 DC‑DC 变换器控制仿真

手把手教你学Simulink——基于平均电流模式(Average Current Mode Control, ACMC)的双向 DC‑DC 变换器控制仿真

目录 手把手教你学Simulink——基于平均电流模式(Average Current Mode Control, ACMC)的双向 DC‑DC 变换器控制仿真 一、为什么要用 平均电流模式控制(ACMC) 二、仿真目标** 三、主电路拓扑与参数** 3.1 拓扑(双向两象限 Buck‑Boost) 3.2 参数表 四、ACMC 控制框…

2026/7/5 15:00:27 阅读更多 →
告别格式障碍:SketchUp STL插件让你的3D设计轻松走进现实世界

告别格式障碍:SketchUp STL插件让你的3D设计轻松走进现实世界

告别格式障碍:SketchUp STL插件让你的3D设计轻松走进现实世界 【免费下载链接】sketchup-stl A SketchUp Ruby Extension that adds STL (STereoLithography) file format import and export. 项目地址: https://gitcode.com/gh_mirrors/sk/sketchup-stl 你是…

2026/7/5 14:58:26 阅读更多 →
4-20mA电流环检测与PIC单片机信号处理方案

4-20mA电流环检测与PIC单片机信号处理方案

1. 4-20mA电流环基础与行业应用工业现场最可靠的信号传输方式莫过于4-20mA电流环,这个看似简单的标准已经统治过程控制领域半个多世纪。电流信号相比电压信号具有显著优势:抗干扰能力强,可长距离传输(理论可达数公里)&…

2026/7/5 14:56:26 阅读更多 →
6. 【C语言】格式化输入输出:和程序说说话

6. 【C语言】格式化输入输出:和程序说说话

前面五篇文章,我们熟悉了变量、常量、数据类型,但程序还像个闷葫芦——要么沉默不语,要么只喊一句固定的“Hello, World”。要让程序真正和人互动,就得学会两样本事: 输出:把数据展示给用户看(…

2026/7/5 14:56:25 阅读更多 →
MWC26 上海开幕,人形机器人点球大战、Agentic AI 成主角——智能体从概念走向赛场

MWC26 上海开幕,人形机器人点球大战、Agentic AI 成主角——智能体从概念走向赛场

MWC26 上海开幕,人形机器人点球大战、Agentic AI 成主角——智能体从概念走向赛场 6 月 24 日,MWC26 上海世界移动通信大会开幕。今年最大的看点不是 5G,不是 6G,而是人工智能。 人形机器人点球大战 MWC26 上海首次举办了"人…

2026/7/5 14:52:25 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻