我的编程空间,编程开发者的网络收藏夹
学习永远不晚

springboot max-http-header-size最大长度的那些事及JVM调优方式

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

springboot max-http-header-size最大长度的那些事及JVM调优方式

问题

线上程序出现了OOM,程序日志中的输出为

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

看线程名称应该是tomcat的nio工作线程,线程在处理程序的时候因为无法在堆中分配更多内存出现了OOM,幸好JVM启动参数配置了-XX:+HeapDumpOnOutOfMemoryError,使用MAT打开拿到的hprof文件进行分析。

第一步就是打开Histogram看看占用内存最大的是什么对象:

可以看到byte数组占用了接近JVM配置的最大堆的大小也就是8GB,显然这是OOM的原因。

第二步看一下究竟是哪些byte数组,数组是啥内容:

可以看到很明显这和HTTP请求相关,一个数组大概是10M的大小。

第三步通过查看GC根查看谁持有了数组的引用:

这符合之前的猜测,是tomcat的线程在处理过程中分配了10M的buffer在堆上。

至此,马上可以想到一定是什么参数设置的不合理导致了这种情况,一般而言tomcat不可能为每一个请求分配如此大的buffer。

第四步就是检查代码里是否有tomcat或服务器相关配置,看到有这么一个配置:

max-http-header-size: 10000000

至此,基本已经确定了八九不离十就是这个不合理的最大http请求头参数导致的问题。

关于http header最大长度的那些事

http协议,超文本传输协议,HyperText Transfer Protocol,是互联网上应用最为广泛的一种网络协议,所有的WWW文件都遵守这个标准。

关于http协议消息的格式,大家可以网上自行搜索,这里不再赘述。

本文关注的是其header部分,如下图所示(红框标注部分):

问题原型

有一个web application提供web service,这个web application基于java开发,部署在tomcat容器上。

问题是:当客户端发送一个GET请求,结果得到400的response,意思是说bad request。

检查了这个request的代码实现逻辑,并没有相关input validation的逻辑,并且检查server端日志发现,request请求似乎并没有到达我们自己代码实现逻辑部分。这是为什么呢?

问题解释

遇到这个问题时,第一步就是查看server端日志,但是觉得很tricky的是,最开始并没有发现相关的日志,只是发现request并没有到达我们自己代码实现逻辑部分。

后来,mina同学眼神很好,发现了如下日志:

通过日志note信息发现,该条日志在info级别下只会打印一次,之后都会是debug级别才打印,难怪之前没有注意到这条日志。

从日志信息可知,request的header部分太大,超过了tomcat允许的最大值。

默认情况下,tomcat(8.0版本)允许的http请求header的最大值是8024个字节(8KB)。

那为什么之前没有出现这个问题呢?

原因是,项目迁移到SCP平台上之后,改成JWT token做权限校验,这个JWT token会被添加到request的header,然而JWT token一般来说都很大(平均有6k个字节左右),所以说在增加了JWT token这个header以及其他一些相关的headers之后,整个request的header部分就超过8024个字节,于是就出现了这个问题。

那么如何解决这个问题呢?可以从两个方面考虑:

增加tomcat允许http header最大值。这个配置参数maxHttpHeaderSize可以设置tomcat允许的http header最大值。

减少header的size,比如不要添加无关的header到request。

扩展

在研究这个问题的过程中,其实还有一些其他疑问。首先,一个request的转发流程大致如下:

那么,在这个流程中,为什么request在前面的部分没有出现这个问题,而这个问题出现在最后一个技术栈是java/tomcat的component呢?

原因是,每个web服务器的http header最大长度的默认值不一样,同时随语言、版本不同也会不一样。举个例子tomcat 5的http header size的默认值是4K。

我找到了其他component中对于http header size的默认值的定义:

CF Router是用Go语言实现,Go语言的http处理模块对于它的定义是默认值1MB。

App Router是用Nodejs实现,Nodejs的http处理模块对它的定义是默认值80KB。

以上两个默认值都要远远大于8KB,这也就解释了没什么问题出在最后一个component。

Tomcat修改maxParameterCount配置

问题

java.lang.IllegalStateException: More than the maximum number of request
 parameters (GET plus POST) for a single request ([10,000]) were detected.
  Any parameters beyond this limit have been ignored.
   To change this limit, set the maxParameterCount attribute
    on the Connector.

解决方案

以前使用外部Tomcat部署项目的时候,可以通过修改server.xml文件中的Connector节点maxParameterCount属性值解决这个问题。

<Connector port=“8080” redirectPort=“8443” protocol=“HTTP/1.1” maxParameterCount="-1" />

因为SpringBoot使用的是内嵌的Tomcat,无法配置server.xml。经过查看相关API文档并没有发现可以直接在配置文件中配置maxParameterCount属性,那么我们就在代码中进行配置,在SpringBoot的API文档中讲解了通过实现WebServerFactoryCustomizer接口可以对Tomcat进行相关配置。

参考

自定义tomcat配置

创建一个类并实现WebServerFactoryCustomizer接口的customize方法。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;


@Component
public class MyTomcatWebServerFactoryCustomizer 
        implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    public static final int DEFAULT_MAX_PARAMETER_COUNT = 10000;

    private Logger logger = LoggerFactory.getLogger(getClass());

    
    @Value("${server.tomcat.maxParameterCount}")
    private int maxParameterCount = DEFAULT_MAX_PARAMETER_COUNT;

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        if (logger.isDebugEnabled()) {
            logger.debug("MyTomcatWebServerFactoryCustomizer customize");
        }

        PropertyMapper propertyMapper = PropertyMapper.get();

        propertyMapper.from(this::getMaxParameterCount)
                .when((maxParameterCount) -> maxParameterCount != DEFAULT_MAX_PARAMETER_COUNT)
                .to((maxParameterCount) -> customizerMaxParameterCount(factory, maxParameterCount));
    }

    
    private void customizerMaxParameterCount(TomcatServletWebServerFactory factory, 
                                             int maxParameterCount) {
        factory.addConnectorCustomizers(
                connector -> connector.setMaxParameterCount(maxParameterCount));
    }

    public void setMaxParameterCount(int maxParameterCount) {
        this.maxParameterCount = maxParameterCount;
    }

    public int getMaxParameterCount() {
        return maxParameterCount;
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

springboot max-http-header-size最大长度的那些事及JVM调优方式

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录