由 max-http-header-size 引起的 OOM
🏷️ Spring Boot
在对服务做压力测试时发现内存消耗非常大,而且一旦开始压测,内存消耗增速非常快,很快就 OOM 了。这个项目因为之前一直请求量很少,所以没有出过问题。经老板提示说之前另一个项目也遇到过类似的问题,当时是由于一个请求头部的配置设置过大导致的。
看了下服务的配置文件,确实有一个 server.max-http-header-size
的配置被设置为了 40485760 (大约是 38.6MB),而在 Spring Boot 中这个配置默认为 8KB。(不清楚为什么要设置这么大,对于 Header 来说 8KB 完全够用了)
java
/**
* Maximum size of the HTTP message header.
*/
private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8);
经调查,在 Http11InputBuffer
初始化时分配了 max-http-header-size
配置指定大小的缓冲区。[1]
java
void init(SocketWrapperBase<?> socketWrapper) {
wrapper = socketWrapper;
wrapper.setAppReadBufHandler(this);
int bufLength = headerBufferSize +
wrapper.getSocketBufferHandler().getReadBuffer().capacity();
if (byteBuffer == null || byteBuffer.capacity() < bufLength) {
byteBuffer = ByteBuffer.allocate(bufLength);
byteBuffer.position(0).limit(0);
}
}
分析了通过 Spring Boot Admin 下载下来的 dump 文件,占用内存最大的 byte[]
确实是在 Http11InputBuffer.SocketInputBuffer
中创建的。
上面截图中的大小是我把 max-http-header-size
设置为 102,400,000 时的数据。
由于这个配置之前是写在代码的配置文件中的,暂时在 Nacos 中添加同名配置项以覆盖代码中的配置。
yaml
server:
max-http-header-size: 8KB
修改配置后貌似不会自动生效,需要重启下服务。
另外,还有一个同名的配置 server.tomcat.max-http-header-size
,这个配置项已经废弃了,应使用 server.max-http-header-size
配置项。如果两个都配置了的话,server.tomcat.max-http-header-size
配置优先。
java
@SuppressWarnings("deprecation")
private DataSize determineMaxHttpHeaderSize() {
return (this.serverProperties.getTomcat().getMaxHttpHeaderSize().toBytes() > 0)
? this.serverProperties.getTomcat().getMaxHttpHeaderSize()
: this.serverProperties.getMaxHttpHeaderSize();
}