• 线上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
    
    • 1
    • 2
    • 3

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

    2. 进行模拟测试

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

    在这里插入图片描述

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    org.apache.coyote.http11.AbstractHttp11Protocol

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

    经查看源码得知,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。
    • 在应用系统中配置参数时,建议理解参数的含义,不能盲目的添加。
    • 上线的系统各个接口及功能,必须要有压力测试。
      在这里插入图片描述
  • 相关阅读:
    2023年高教社杯数学建模国赛C题详细版思路
    telnet的使用
    现在的关系数据库都是怎么工作的
    Hive学习笔记2
    Vue----全局组件和局部组件
    大厂面试之算法篇
    接口测试常见问题解答
    用Promise发起请求,失败后再次请求,几次后不再执行
    猴子选大王[加强版]
    kubernetes-pod的驱逐策略
  • 原文地址:https://blog.csdn.net/lsdaini/article/details/132637746