Fork me on GitHub

Tomcat调优教程

目录

TIPS

本文基于Tomcat 9.0编写,理论兼容Tomcat 8.x及更高版本。

本文来探讨Tomcat的调优。

Tomcat调优参数

主要调优参数

在做Tomcat的调优时,最重要是就是Connector(连接器)的调优了(少数情况下,也可能会配置Executor)

下面贴出一段server.xml中的配置:

1
2
3
4
5
6
7
8
9
10
11
12
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="150"
minSpareThreads="4"/>

<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
executor="tomcatThreadPool"/>

一个文件中可以有多个Connector以及多个Executor。

其中:

  • Connector:负责接收客户端的请求以及向客户端回送响应的消息
  • Executor:指定线程池,从而在多个Connector(主要)及其他组件之间共享

TIPS

对执行原理感兴趣的童鞋可以参阅下这篇文章,写得相当棒:https://blog.csdn.net/zzzgd_666/article/details/88740198 。由于本文重点在于“调优”,所以就不赘述了。

Connector常用属性

Connector负责接收客户端的请求以及向客户端回送响应的消息。Tomcat 9.0有三类Connector:

  • HTTP/1.1(HTTP Connector)
  • HTTP/2(HTTP2 Upgrade Protocol)(Tomcat 8.5引入)
  • AJP(AJP Connector)

但不管哪种Connector,工作过程都是类似的:

  • 每个请求都需要1个线程去处理
  • 如果接收到的并发请求 > 当前处理请求的线程所能处理的数量,则创建其他线程去处理,直到达到maxThreads为止
  • 如果线程数达到maxThreads设置的值后,依然接收到更多的请求,那么请求将会堆积在Connector创建的server socket中(即accept队列),直到达到acceptCount的值为止。如果堆积的请求数目达到acceptCount后,依然受到更多的请求,那么直接返回connection refused

常用属性如下:

TIPS

  • 加粗的表示Tomcat调优常用的参数
  • 这里只列出了Connector常用属性,要想阅读完整属性,可使用如下方式:
    • 方式一、访问 http://tomcat.apache.org/tomcat-9.0-doc/config/http.html ,将9.0修改为你的Tomcat版本即可,比如想看Tomcat 8的文档,可将9.0改为8.0;
    • 方式二、下载Tomcat并启动,访问 /docs/config/http.html 路径即可。例如:http://localhost:8080/docs/config/http.html
  • minSpareThreads:始终保持运行状态的线程数,默认值10。即便超过了最大空闲时间(maxIdleTime),也不会被回收。如果配置了Executor,将忽略此属性

  • maxThreads:Connector创建来处理请求的最大线程数,此参数决定了可以同时处理的请求的最大数量,默认200。如果配置了Executor,将忽略此属性。超过则放入请求队列中进行排队。

  • maxConnections:Tomcat在任意时间接收和处理的最大连接数。当连接数达到maxConnections时,仍可基于acceptCount的配置接受连接,但并不会处理,直到Tomcat接收的连接数小于maxConnections。

    默认值与Connector使用的协议有关:

    • NIO的默认值是10000
    • APR的默认值是8192
    • BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)
    • 在Windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。如果设置为-1,则连接数不受限制。
  • connectionTimeout:网络连接超时时间,单位毫秒,默认60000。设为0表示永不超时,一般不建议。除非将disableUploadTimeout设置为false,否则读取request body时也会使用该超时。

  • acceptCount:当最大线程数(maxThreads)被使用完时,可以放入请求队列的队列长度,默认100。一旦队列满了,就会返回connection refused。因此,如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。一般可设置成和maxThreads相同,但具体还需根据自己的应用实际访问峰值和平均值来权衡。

  • enableLookups:是否启用DNS查找功能。如果设为true,会用request.getRemoteHost()执行DNS lookup,从而返回远程客户端的主机名。设为false则跳过DNS lookup,并以字符串形式返回IP地址,从而提高性能,默认false,生产环境建议保持关闭。

  • compression:是否开启GZIP压缩。取值off(禁用)、on(打开,压缩文本数据),force(强制压缩所有格式)、数字(表示数据量达到该值就GZIP传输)。

  • port:指定Tomcat监听的端口

  • protocol:为Connector设置使用什么协议处理入口流量,默认值 HTTP/1.1 ,使用此值的话,对于Tomcat 8及更高版本,会自动根据当前情况,使用基于NIOConnector或基于APR的Connector;对Tomcat 7及更低版本会自动根据情况使用基于BIO的Connector或基于APR的Connector。如果不想自动切换,也可人工指定协议(从Tomcat 8开始,一般无需人工设置):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // BIO,这种模式下,使用传统的I/O操作(即java.io包及其子包),性能较差。
    protocol="org.apache.coyote.http11.Http11Protocol"

    // NIO,使用NIO操作(即java.nio包及其子包),比传统I/O拥有更好的并发性能
    protocol="org.apache.coyote.http11.Http11NioProtocol"

    // NIO2,使用NIO2操作(NIO2是JDK 7引入的特性),Tomcat 8引入
    protocol="org.apache.coyote.http11.Http11Nio2Protocol"

    // APR,使用APR操作。APR即Apache Portable Runtime,这是一个Apache HTTP服务器的支持库。你可以认为:此模式下,Tomcat将以JNI的形式调用 Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而提高Tomcat对静态文件的处理性能
    protocol="org.apache.coyote.http11.Http11AprProtocol"

    下表展示了不同Connector之间的区别:

    - Java Nio Connector NIO Java Nio2 Connector NIO2 APR/native Connector APR
    Classname Http11NioProtocol Http11Nio2Protocol Http11AprProtocol
    Tomcat Version since 6.0.x since 8.0.x since 5.5.x
    Support Polling YES YES YES
    Polling Size maxConnections maxConnections maxConnections
    Read Request Headers Non Blocking Non Blocking Non Blocking
    Read Request Body Blocking Blocking Blocking
    Write Response Headers and Body Blocking Blocking Blocking
    Wait for next Request Non Blocking Non Blocking Non Blocking
    SSL Support Java SSL or OpenSSL Java SSL or OpenSSL OpenSSL
    SSL Handshake Non blocking Non blocking Blocking
    Max Connections maxConnections maxConnections maxConnections

    TIPS

    表格来自 http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Connector_Comparison

  • connectionUploadTimeout:指定上传时的超时。disableUploadTimeout需设置成false才有效

  • disableUploadTimeout:设置为true,上传超时使用connectionTimeout,设置成false,上传超时使用connectionUploadTimeout。默认true

  • redirectPort:表示安全通信(https)转发端口

  • executor:指定executor名称。如果设置了此属性,则Connector将使用该Executor执行程序。如果不设置executor属性,则Connector将使用内部私有的Executor来提供线程池。该属性主要用来实现在多个Connector及其他组件之间共享线程池。

    TIPS

    压缩带来的好处是减少带宽,但缺点在于增加了服务器的CPU开销。就笔者个人的经验,很少直接用Tomcat的GZIP功能,更多使用NGINX的GZIP。

maxConnections、maxThreads、acceptCount之间的关系

我们把Tomcat想象成是一个餐厅,请求就像是去就餐的顾客。这个餐厅非常火爆,当处理不过来的时候,就会排号。

  • acceptCount

    可以类比为餐厅的排号处能够容纳排号的最大数量,排号的数量不是无限制的,一旦达到上限,就说已经客满(直接返回connection refused)

  • maxConnections
    可以类比为餐厅的大堂的餐桌数量,也就是可以就餐的桌数。

    • 如果当前连接数尚未达到maxConnection,说明还有空闲的餐桌,直接上桌即可。
    • 如果所有餐桌都已经坐满,但是排号人数尚未达到 acceptCount,那么就排个号,等着叫号(此时,尽管还能接受请求,但是不会处理你的请求,除非有桌子空闲了)
    • 如果取号的人数已经达到acceptCount,则取号失败,直接拒绝服务(直接返回connection refused)
  • maxThreads
    可以类比为厨师的个数。厨师一开始只有10个(最少minSpareThreads个),如果发现忙不过来的时候,就会增加几个厨师,一致增加到maxThreads个厨师。如果还是不够,那就只能慢慢等着上菜了。这就像是你去吃饭,有时候上一道菜都吃完了,下一道菜还没有上。只不过,在计算机的世界,厨师的数量也不能无止境地增加下去。因为线程数目只要超过CPU核心数,就会存在CPU切换的开销,线程数越多,切换开销越大,所以说maxThreads设置也不能太大。

Executor常用属性

定义线程池,从而在多个Connector(主要)及其他组件之间共享。Executor必须实现org.apache.catalina.Executor接口。

常用属性如下:

TIPS

这里只列出了Connector常用属性,,要想阅读完整属性,可使用如下方式:

  • 方式一、访问 http://tomcat.apache.org/tomcat-9.0-doc/config/executor.html ,将9.0修改为你的Tomcat版本即可,比如想看Tomcat 8的文档,可将9.0改为8.0
  • 方式二、下载Tomcat并启动,访问 /docs/config/executor.html 路径即可。例如:http://localhost:8080/docs/config/executor.html
  • className:Executor的实现类。必须实现org.apache.catalina.Executor接口。默认值为org.apache.catalina.core.StandardThreadExecutor

  • name:Executor名称,必填且必须唯一

  • threadPriority:指定线程的优先级,默认5(Thread.NORM_PRIORITY)

  • daemon:是否为守护线程,默认true

  • namePrefix:指定Executor创建的线程的名称前缀。线程名称格式为namePrefix+threadNumber

  • maxThreads:活动线程的最大数量,默认200

  • minSpareThreads:使用保持活动状态的最小线程数(空闲和活动),默认25

  • maxIdleTime:线程最大空闲时间,单位毫秒,默认60000(1分钟)。达到该时间后就会把该线程关闭(当然如果当前活动线程数 < minSpareThreads不会关闭)

  • maxQueueSize:拒绝执行之前可以排队等待执行的任务数量,默认Integer.MAX_VALUE

  • prestartminSpareThreads:在启动Executor时是否就启动minSpareThreads个线程,默认false

  • threadRenewalDelay:如果配置了ThreadLocalLeakPreventionListener,它将会通知Executor有关上下文停止的信息。一旦上下文停止后,线程池中的线程将会被更新。为了避免同时更新线程,可用此属性设置更新的延迟。默认1000,单位毫秒,如果设成负数,则线程不会被更新。

其他调优参数

Host

Host表示虚拟主机。

server.xml

1
2
<Host name="localhost" autoDeploy="true">
</Host>

这里只列出了Host的调优属性,,要想阅读完整属性,可使用如下方式:

  • 方式一、访问 http://tomcat.apache.org/tomcat-9.0-doc/config/host.html ,将9.0修改为你的Tomcat版本即可,比如想看Tomcat 8的文档,可将9.0改为8.0
  • 方式二、下载Tomcat并启动,访问 /docs/config/host.html 路径即可。例如:http://localhost:8080/docs/config/host.html
  • autoDeploy:是否要让Tomcat周期性检查新的或更新了的Web应用程序。如果设为true,则Tomcat会定期检查appBase和xmlBase目录,并自动部署。默认true。由于autoDeploy=true时,会启用一个定时任务,如果没有“自动部署”的需求,可将其设为false。

Spring Boot/Spring Cloud项目支持的调优参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server:
# 等价于Connector.connectionTimeout
connection-timeout: 60s
tomcat:
# 等价于Connector.acceptCount
accept-count: 100
# 等价于Connector.maxConnections
max-connections: 10000
# 等价于Connector.maxThreads
max-threads: 200
# 等价于Connector.minSpareThreads
min-spare-threads: 10
# TIPS:压缩带来的好处是减少带宽,但缺点在于增加了服务器的CPU开销。就笔者个人的经验,很少直接用Tomcat的GZIP功能,更多使用NGINX的GZIP。
compression:
# 是否开启GZIP,默认关闭
enabled: false
# 执行压缩所需的最小响应大小,默认2KB
min-response-size: 2KB
# 想要GZIP的格式,默认"text/html", "text/xml", "text/plain",
"text/css", "text/javascript", "application/javascript", "application/json",
"application/xml"
mime-types: "text/html", "text/xml"

其他调优

Tomcat Session调优

一、JSP与Session

如果使用JSP的话,如果业务允许,考虑考虑加上以下内容:

1
<%@ page session="false" %>

高并发场景下,可能会导致内存溢出。

拓展阅读

一个内存溢出的问题定位过程:https://blog.csdn.net/zhangm3256/article/details/83197756

二、为Session设置合理的超时

超时时间不宜过长,否则也可能会造成内存上的压力。

  • 对于传统Tomcat项目,只需在应用的 web.xml 中添加如下内容:

    1
    2
    3
    4
    <!-- 配置Session失效时间,单位分钟 -->  
    <session-config>
    <session-timeout>30</session-timeout>
    </session-config>
  • 对于Spring Boot项目,通过如下配置设置Session即可。

    1
    2
    3
    4
    server:
    servlet:
    session:
    timeout: 30m

    拓展阅读

    其他设置Session超时时间的方法详见 https://www.iteye.com/blog/jiangshuiy-1843622

相关文章

评论系统未开启,无法评论!