热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springboot maxhttpheadersize最大长度的那些事及JVM调优方式【java基础】

这篇文章主要介绍了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属性值解决这个问题。

因为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;

/**
 * 自定义Tomcat容器配置类
 *
 */
@Component
public class MyTomcatWebServerFactoryCustomizer 
        implements WebServerFactoryCustomizer {

    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));
    }

    /**
     * 配置内置Tomcat单次请求参数限制
     *
     * @param factory
     * @param 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;
    }
}

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


推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • Elasticsearch1Elasticsearch入门1.1Elasticsearch术语1.1.16.0以前的Elasticsearch术语1.1.26.0以后的Elasti ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
author-avatar
chucai
这个家伙很懒,什么也没留下,只留下了这个默认个签!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有