1. 一个“离谱”的配置引发的血案
那天下午,监控系统突然开始疯狂报警,手机嗡嗡响个不停。我一看,是线上一个核心的Web服务,CPU和内存曲线都拉成了一条直线,紧接着就是大量的“java.lang.OutOfMemoryError: Java heap space”错误日志刷屏。服务几乎处于不可用状态,情况非常紧急。
第一时间,我们当然是先重启实例,恢复服务。但问题必须根除,不然就是颗定时炸弹。重启后,我们立刻下载了OOM时自动生成的堆内存快照文件(Heap Dump),用MAT(Memory Analyzer Tool)这个神器打开分析。不看不知道,一看吓一跳。在支配树(Dominator Tree)和直方图(Histogram)里,赫然发现有好几个巨无霸对象,每个都占用了接近200MB的内存,而且它们的类型都是 org.apache.coyote.http11.Http11OutputBuffer。
这太反常了。Http11OutputBuffer 是Tomcat内部用来处理HTTP响应头的缓冲区,正常来说,它的大小也就几KB到几十KB,怎么可能达到200MB?这感觉就像是你家厨房的洗菜池,突然被改造成了游泳池的规模,不仅浪费空间,而且一旦同时开几个水龙头,整个家都得被淹了。
我们顺着这个线索,去检查了服务的配置文件。当看到 application.yml 里那一行配置时,我简直哭笑不得:
server:
tomcat:
max-http-header-size: 200MB
是的,你没看错,server.max-http-header-size 被设置成了 200MB。配置这个参数的同学,可能是出于“以防万一”的心态,觉得把缓冲区设大点总没坏处,能兼容各种“变态”的请求头。但正是这个“好心”,直接导致了这次线上OOM事故。这就像为了接住偶尔可能掉下来的苹果,你每天顶着一个体育馆那么大的盘子出门,不累垮才怪。
那么,这个参数到底是怎么工作的?为什么一个简单的配置错误,就能让内存瞬间崩溃?这背后是Tomcat处理HTTP协议的核心机制,也是我们今天要掰开揉碎讲清楚的重点。
2. 解剖Tomcat的“咽喉要道”:Http11OutputBuffer
要理解 max-http-header-size 的威力,我们得先看看Tomcat是怎么处理HTTP请求的。你可以把Tomcat想象成一个高效的邮局分拣中心。当网络数据包(信件)到达后,需要先拆开信封,读取收件人、寄件人等信息(HTTP头部),然后再处理信件正文(HTTP Body)。
Http11OutputBuffer 就是这个分拣中心里,专门用来封装和暂存“寄件人信息”(HTTP响应头) 的工作台。当Tomcat处理完一个请求,准备发送响应给客户端时,所有的响应头信息(比如 Content-Type, Set-Cookie, Cache-Control 等等)都需要先被整理、编码,然后才能通过网络发出去。这个整理和暂存的地方,就是 Http11OutputBuffer。
它的核心是一块内存区域,在Java里,就是用 ByteBuffer 来实现的。关键代码就在 Http11OutputBuffer 的构造函数里,我们结合原始文章里的代码片段,用更直白的方式解释一下:
protected Http11OutputBuffer(Response response, int headerBufferSize) {
th


2653

被折叠的 条评论
为什么被折叠?



