线上HTTP请求导致OOM【max-http-header-size配置不当引起】

起源

新上线的一个项目,运行了半个月,突然出现频繁宕机(有大量的第三方接口请求业务),严重影响了业务系统的使用。

项目环境

Linux (3.10.0-957.el7.x86_64)
open JDK 1.8.32
SpringBoot 2.2.6.RELEASE
内嵌 Tomcat 9.0.47
配置最大堆内存配置 16G

分析问题

1. 检查日志信息

通过对日志检查,发现有下面的报错信息,从错误日志看,是由于出现了OOM错误,堆内存溢出导致的问题。

Exception in thread "http-nio-8080-exec-1011" java.lang.OutOfMemoryError: Java heap space
Exception in thread "http-nio-8080-exec-1031" java.lang.OutOfMemoryError: Java heap space
Exception in thread "http-nio-8080-exec-1052" java.lang.OutOfMemoryError: Java heap space

由于不知道程序是哪一部分的代码或者配置出现的问题,所以计划模拟OOM场景,并导出dump文件进一步分析。

2. 进行模拟测试

  1. 在idea中配置出现OOM的jvm参数,如下所示:
-Xms1024M -Xmx1024M -XX:+HeapDumpOnOutOfMemoryError

在这里插入图片描述

  1. 通过JMeter工具,模拟生产环境,开启十个线程发送1000笔请求同时访问接口,发现复现了以上的OOM,并在项目根目录生成了内存快照java_pid4521.hprof。

  2. 使用VisualVM工具打开,发现20个实例竟然占据了98%的大小,其中byte数组中占用了大量的资源,展开细看发现有很多Http11InputBuffer和Http11OutputBuffer实例,经查阅资料得知,均是关于网络请求缓存一类的。
    Http11InputBuffer在这里插入图片描述Http11OutputBuffer
    在这里插入图片描述

3. 查看Tomcat源码

org.apache.coyote.http11.Http11Processor#Http11Processor

    public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
        super(adapter);
        this.protocol = protocol;

        httpParser = new HttpParser(protocol.getRelaxedPathChars(),
                protocol.getRelaxedQueryChars());

        inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
                protocol.getRejectIllegalHeaderName(), httpParser);
        request.setInputBuffer(inputBuffer);

        outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
        response.setOutputBuffer(outputBuffer);

org.apache.coyote.http11.AbstractHttp11Protocol

    /**
     * Maximum size of the HTTP message header.
     */
    private int maxHttpHeaderSize = 8 * 1024;
    public int getMaxHttpHeaderSize() { return maxHttpHeaderSize; }

经查看源码得知,tomcat对于inputBuffer和outputBuffer的HTTP请求header大小,初始化默认都是8 * 1024。

4. 检查系统

在代码里进行了全局检索,终于发现了类似于header的配置:server.max-http-header-size,设置的为102M。一次inputBuffer就会有一次outputBuffer,即每次请求相当于占用了双倍,没及时释放就会积累,况且JVM分配的内存和本身物理机器空间也不是无限的,当请求数量过多,必然会出现内存溢出的问题,到此,基本已经确定了,就是这个不合理的最大http请求头参数导致的问题。

解决方案

我们把如下配置文件中的参数去掉,保留默认的配置,进行压力测试后,发现没有出现过OOM现象,自此该问题得以解决。

> server.max-http-header-size=102400000

名词解释

  • OOM:指应用系统中存在无法回收的内存或使用的内存过多,导致的内存溢出。
  • 堆内存:是JVM中用于存放对象的一块内存区域,Java程序中所有new出来的对象都会被存放在堆内存中。
  • JMeter:自动化测试工具,官网链接:https://jmeter.apache.org/download_jmeter.cgi
  • VisualVM:性能分析工具,官网链接:https://visualvm.github.io/

小结

  • max-http-header-size参数默认8Kb。
  • 在应用系统中配置参数时,建议理解参数的含义,不能盲目的添加。
  • 上线的系统各个接口及功能,必须要有压力测试。
    在这里插入图片描述
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Docker提供了一个选项来设置容器不被操作系统杀死,该选项称为"oom-kill-disable"。oom-kill-disable是一个用于禁用内存超限时杀死容器的选项。 当容器内存使用量超出其可用内存的限制时,操作系统会触发一个内存超限(OOM)事件。通常情况下,为了从OOM事件中恢复,操作系统会选择杀死占用内存较大的进程(包括容器)来释放内存资源。 如果我们想禁止Docker容器在OOM事件中被杀死,我们可以使用"oom-kill-disable"选项。这个选项可以在运行容器时通过命令行或Docker Compose文件进行设置,具体的使用方法如下: 1. 在使用命令行运行容器时,添加"--oom-kill-disable"选项,例如: ``` docker run --oom-kill-disable <容器名称或ID> ``` 2. 在Docker Compose文件中,为容器配置"oom_kill_disable"字段并设置为true,例如: ```yaml services: myservice: oom_kill_disable: true ``` 注意,禁用容器的OOM事件可能导致主机操作系统的整体性能下降,甚至会影响到其他容器的正常运行。因此,在使用"oom-kill-disable"选项时需要仔细评估容器内存使用及主机资源的情况,确保系统的稳定性和可用性。 最后,需要注意的是,对于大多数情况来说,允许操作系统根据OOM事件决定杀死容器是合理的做法,因为这有助于保持系统的可用性和可靠性,并防止整个系统由于单个容器的内存消耗过大而崩溃。所以,在设置"oom-kill-disable"选项时,需要慎重考虑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一沐南风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值