欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tomcat 基础优化

程序员文章站 2022-06-15 14:42:14
...

本文档是身边一些朋友、技术大佬之前分享的一些笔记,记录了 Tomcat 优化方法,笔记较多而且比较杂乱,经过整理、分类我个人觉得大致可以从以下几个方面优化 Tomcat:

  1. Tomcat 运行模式
  2. Tomcat 配置优化
  3. JVM 优化
  4. 内核参数优化

一、修改 Tomcat 运行模式

Tomcat Connector有三种运行模式:

  • bio:阻塞 IO bio 是三种运行模式中性能最低第一种。

  • nio:是一个基于缓冲区,并能提供非阻塞 I/O 操作的 JAVA API 因此 NIO 也成为非阻塞 I/O,比 bio 拥有更好的并发性能(默认是 nio)。

  • apr:调用 httpd 核心链接库来读取或文件传输,从而提高 tomcat 对静态文件的处理性能。Tomcat APR 模式也是 Tomcat 在高并发下的首选运行模式。

APR 模式文档链接:http://tomcat.apache.org/tomcat-8.5-doc/apr.html

1.1 安装 apr 和 apr-util

Apache Portable Runtime 是一个高度可移植的库,是 Apache HTTP Server 2.x 的核心。APR 有许多用途,包括访问高级 IO 功能(如sendfile,epoll 和 OpenSSL),操作系统级功能(随机数生成,系统状态等)和本机进程处理(共享内存,NT 管道和 Unix 套接字)。

$ cd /usr/local/src/
$ wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-1.6.5.tar.gz
$ wget http://mirrors.tuna.tsinghua.edu.cn/apache//apr/apr-util-1.6.1.tar.gz
$ tar xf apr-1.6.5.tar.gz
$ cd apr-1.6.5
$ /configure --prefix=/usr/local/apr
$ make -j 2 && make install 
$ cd ../
$ tar xf apr-util-1.6.1.tar.gz
$ cd apr-util-1.6.1
$ ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr/

1.2 安装 tomcat-native

tomcat-native 不用单独下载,解压缩 tomcat 程序包后再 bin/ 目录下存在该程序的源码包。

$ tar xf apache-tomcat-8.5.38.tar.gz -C /usr/local/
$ ln -sv /usr/local/apache-tomcat-8.5.38/ /usr/local/tomcat
$ cp /usr/local/tomcat/bin/tomcat-native.tar.gz /usr/local/src/
$ tar xf tomcat-native.tar.gz
$ cd tomcat-native-1.2.21-src/
$ cd native/
$ ./configure --prefix=/usr/local/apr --with-java-home=/usr/local/jdk1.8.0_121/
$ make -j 2 && make install
$ vim /etc/profile
export LD_LIBRARY_PATH=/usr/local/apr/lib ##添加apr path
$ source /etc/profile

1.3 修改 Tomcat 配置文件

1、修改 protocol 值

Tomcat 默认是 "HTTP/1.1" ,如果运行 apr 模式需要把 protocol 值修改成 apr 模式:org.apache.coyote.http11.Http11AprProtocol 参考官方文档中 protocol 说明:http://tomcat.apache.org/tomcat-8.5-doc/config/http.html#HTTP/1.1_and_HTTP/1.0_Support

$ cd /usr/local/tomcat/conf/
$ vim server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
               connectionTimeout="20000"
               redirectPort="8443" />

2、修改 SSLEngine

<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" />

1.4 启动 Tomcat 验证

查看 catalina.out 日志中最下面三行

Tomcat 基础优化

二、Tomcat 配置优化

2.1 增大随机数熵池

你是否遇到过当你执行了 ./startup.sh 或者 ./catalina.sh start 后,访问你的服务会一直转啊转啊,可能要几分钟才能正常提供服务。

原因:

在apache-tomcat 官方文档:如何让 tomcat 启动更快里面提到了一些启动时的优化项,其中一项是关于随机数生成时,采用的 "熵源"(entropy source)的策略。提到 tomcat7 的 session id 的生成主要通过 java.security.SecureRandom 生成随机数来实现,随机数算法使用的是 ”SHA1PRNG” 文章具体内容见下图

Tomcat 基础优化

在 http://wiki.apache.org/tomcat/HowTo/FasterStartUp (Entropy Source 部分)有一段解释。* 上面也有一大批这方面的说明,所以这里就不再多做介绍。

2.1.1 使用伪随机函数生成器

通过修改 Tomcat 启动文件 -Djava.security.egd=file:/dev/urandom
通过修改 JRE 中的 java.security 文件 securerandom.source=file:/dev/urand

2.1.2 增大/dev/random的熵池(推荐)

安装熵池服务 rngd

$ yum -y install rng-tools
$ systemctl start rngd
$ systemctl enable rngd

启动服务后观察 cat /proc/sys/kernel/random/entropy_avail 基本在三千左右。

2.2 隐藏 Tomcat 版本号

隐藏 Tomcat 版本号,不在优化内容范围内,出于安全角度建议大家隐藏。

修改前如下:

Tomcat 基础优化

$ cd /usr/local/tomcat/lib/
$ jar xf catalina.jar
$ cd org/apache/catalina/util/
$ vim ServerInfo.properties
# 修改下面信息
server.info=Apache Tomcat/8.5.38
server.number=8.5.38.0
server.built=Feb 5 2019 11:42:42 UTC
修改后为
server.info=Apache Tomcat
server.number=0.0.0.0
server.built=Feb 5 2019 11:42:42 UTC

将修改后的信息压缩回 jar 包:

$ cd  /usr/local/tomcat/lib
$ jar uvf catalina.jar org/apache/catalina/util/ServerInfo.properties
正在添加: org/apache/catalina/util/ServerInfo.properties(输入 = 885) (输出 = 512)(压缩了 42%)

# 重启 Tomcat

修改后再查看如下:

Tomcat 基础优化

2.3 server.xml 配置文件优化

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="40000"
           maxHttpHeaderSize="32768"
           redirectPort="8443"
           maxThreads="1000"
           minSpareThreads="100" 
           maxSpareThreads="1000"
           acceptorThreadCount="2" 
           acceptCount="2000"
           minProcessors="100"
           maxProcessors="2000"
           enableLookups="false"
           maxKeepAliveRequests="-1"
           keepAliveTimeout="-1"
           disableUploadTimeout="false"
           connectionUploadTimeout="150000"
           useSendfile="false" 
           URIEncoding="UTF-8" />

配置参数说明

protocol="HTTP/1.1"

maxHttpHeaderSize="8192"

maxThreads="1000" //最大线程数,默认200

minSpareThreads="100" //Tomcat初始化时创建的socket线程数,线程的最小运行数目,这些始终保持运行,如果未指定,默认值为10

enableLookups="false"//关闭DNS反向查询,若设为true,则支持域名解析,可把ip地址解析为主机名

compression="on"//打开压缩功能

compressionMinSize="2048"

compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"

connectionTimeout="20000"//代表连接超时时间,单位为毫秒,默认值为60000。通常情况下设置为30000

URIEncoding="utf-8"//URL统一编码

acceptCount="1000"//监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads),如果未指定,默认值为100

redirectPort="8443"//在需要基于安全通道的场合,把客户请求转发到基于SSL的redirectPort端口

disableUploadTimeout="true"/>//这个标志允许servlet[Container](http://lib.csdn.net/base/4)在一个servlet执行的时候,使用一个不同的,更长的连接超时。最终的结果是给servlet更长的时间以便完成其执行,或者在数据上载的时候更长的超时时间。如果没有指定,设为false

禁用 AJP(如果你服务器没有使用 Apache)

<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->

Tomcat 压缩配置,建议在前端 nginx 上开启压缩。Tomcat 作为应用服务器本身就很繁忙了。

compression="on"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,application/javascript,application/json,text/javascript,text/css,text/plain,image/gif,image/jpg,image/png"

三、JVM 优化

Java 程序调优,主要就是调『堆内存』和『垃圾回收机制』。

3.1 JVM 调优常用参数说明

参数 作用
-Xms 堆初始内存大小
-Xmx 堆最大内存大小
-Xmn 初始新生代内存大小,一般设置为整个堆的 1/3到 1/4 左右
-XX:PrintGC 每次触发GC的时候打印相关日志
-XX:+PrintGCDetails 更详细的GC日志
-XX:SurvivorRatio=2 用来设置新生代中 Eden 空间和 from/to 空间比例,默认是 8 (Edem : from : to = 8 : 1 : 1)
-Dfile.encoding=UTF-8 设置字符集避免日志中出现乱码
-Dsun.jnu.encoding=UTF-8 设置字符集避免日志中出现乱码
-Duser.timezone=GMT+08 时区设置
-XX:+HeapDumpOnOutOfMemoryError 堆异常报错输出
-XX:HeapDumpPath=path 设置在dump heap时将文件dump到哪里。默认是当前目录下 java_pidpid.hprof这样形式的文件。
-XX:+UseParallelGC 选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
-XX:+UseParallelOldGC 配置年老代垃圾收集方式为并行收集。
-XX:ParallelGCThreads=8 设置支持并发GC的线程数。

3.2 调优总结

在实际工作中,我们可以直接将初始堆大小(-Xms)与最大堆大小(-Xmx) 配置相等,这样的好处就是可以减少程序运行时垃圾回收的次数,从而提高效率。

新生代与老年代的设置比例:1:3 或者 1:4

四、内核参数优化

以下内核参数是工作环境中大家经常会看到的一些:

net.core.netdev_max_backlog = 32768
net.core.somaxconn = 32768
net.core.rmem_default = 8388608
net.core.wmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.ip_local_port_range = 1024  65535
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_mem = 94500000 915000000   927000000
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 65535

以上内核参数作用说明:

# cat /proc/sys/net/core/netdev_max_backlog
# 默认值:1000
# 作用:网卡设备将请求放入队列的长度
net.core.netdev_max_backlog = 32768

# cat /proc/sys/net/core/somaxconn
# 默认值:128
# 作用:已经成功建立连接的套接字将要进入队列的长度
net.core.somaxconn = 32768

# cat /proc/sys/net/core/rmem_default
# 默认值:212992
# 作用:默认的TCP数据接收窗口大小(字节)
net.core.rmem_default = 8388608

# cat /proc/sys/net/core/wmem_default
# 默认值:212992
# 作用:默认的TCP数据发送窗口大小(字节)
net.core.wmem_default = 8388608


# cat /proc/sys/net/core/rmem_max
# 默认值:212992
# 作用:最大的TCP数据接收窗口大小(字节)
net.core.rmem_max = 16777216

# cat /proc/sys/net/core/wmem_max
# 默认值:212992
# 作用:最大的TCP数据发送窗口大小(字节)
net.core.wmem_max = 16777216

# cat /proc/sys/net/ipv4/ip_local_port_range
# 默认值:32768   61000
# 作用:可用端口的范围
net.ipv4.ip_local_port_range = 1024  65535

# cat /proc/sys/net/ipv4/route/gc_timeout
# 默认值 300
# 作用:路由缓存刷新频率,当一个路由失败后多长时间跳到另一个路由
net.ipv4.route.gc_timeout = 100

# cat /proc/sys/net/ipv4/tcp_fin_timeout
# 默认 60
# 作用:表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout = 30 

# cat /proc/sys/net/ipv4/tcp_keepalive_time 
# 默认值:7200
# 作用:间隔多久发送1次keepalive探测包
net.ipv4.tcp_keepalive_time = 1200  

# cat /proc/sys/net/ipv4/tcp_timestamps 
# 默认值:1
# 作用:TCP时间戳,时间戳必须要开启,否则下面的 TIME_WAIT 重用和快速回收无效
net.ipv4.tcp_timestamps = 1

# cat /proc/sys/net/ipv4/tcp_tw_recycle
# 默认值:0
# 作用:表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_tw_recycle = 1

######################## cat /proc/sys/net/ipv4/tcp_tw_reuse
# 默认值:0
# 作用:针对 TIME-WAIT,做为客户端可以启用(例如,作为nginx-proxy前端代理,要访问后端的服务)
net.ipv4.tcp_tw_reuse = 1

# cat /proc/sys/net/ipv4/tcp_syn_retries
# 默认值 2
# 作用:在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_syn_retries = 1

# cat /proc/sys/net/ipv4/tcp_synack_retries
# 默认值 2
# 作用:为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_synack_retries = 1

# cat /proc/sys/net/ipv4/tcp_mem
#确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。
#第一个值是内存使用的下限。
#第二个值是内存压力模式开始对缓冲区使用应用压力的上限。
#第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
net.ipv4.tcp_mem = 94500000 915000000   927000000

# cat /proc/sys/net/ipv4/tcp_max_orphans
# 默认值 16384
# 作用:系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单 的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。
net.ipv4.tcp_max_orphans = 3276800

# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
# 默认值:128
# 作用:增大SYN队列的长度,容纳更多连接
net.ipv4.tcp_max_syn_backlog = 65535

五、JVM 调优常用工具

5.1 为 Tomcat 配置 JMX 连接

开启 JMX 远程连接以后,就可以通过 jconsole 和 jvisualvm 两个 JDK 内置工具连接 JMX 从而查看当前主机的 JVM 相关信息。

$ cd /usr/local/tomcat/bin/
# 默认情况下,$CATALINA_HOME/bin 目录下是没有 setenv.sh,可以自己新建此文件
$ vim setenv.sh
CATALINA_OPTS="-Djava.rmi.server.hostname=10.100.4.169
-Dcom.sun.management.jmxremote.port=8686
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false"
:wq

JMX 配置说明:

# 通过哪个 IP 访问本机的 JMX 
-Djava.rmi.server.hostname=10.100.4.169
# JMX 监听的端口,默认 8686
-Dcom.sun.management.jmxremote.port=8686
# 是否需要用户名密码认证,默认 false
-Dcom.sun.management.jmxremote.authenticate=false
# 是否启用 SSL
-Dcom.sun.management.jmxremote.ssl=false"

5.2 使用 jconsole 工具连接 JMX

Jconsole 是 JDK 自带的监控工具,在 JDK/bin 目录下可以找到。它用于连接正在运行的本地或者远程的 JVM,对运行在 java 应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。

当你在 Windows 或者 Mac 下运行 jconsole 后就会在桌面弹出 jconsole 的控制台,选择远程连接,输入IP:端口 点击连接

Tomcat 基础优化

我这里在配置 JMX 的时候没有启用 SSL 所以会提示不安全,点击『不安全的连接』

Tomcat 基础优化

Tomcat 基础优化

5.3 使用 jvisualvm 工具连接 JMX

jvisualvm 同样是 JDK 内置的工具,使用方法与 jconsole 类似。功能上要比 jconsole 强大一些。推荐使用。

运行 jvisualvm 后同样在桌面弹出一个控制台窗口

Tomcat 基础优化

远程 --> 添加主机 --> 添加 JMX 连接

Tomcat 基础优化

Tomcat 基础优化

5.4 jmap 工具

jmap 可以输出 Java 进程 内存中对象的工具。jmap 一般和 jhat 或者 MAT 配合使用,以图像的形式直观的展示当前内存是否有问题。

5.4.1 参数说明

-dump:[live,]format=b,file=<filename>
    以hprof二进制格式转储Java堆到指定filename的文件中。
    live子选项是可选的,如果指定了live子选项,堆中只有活动的对象会被转储。
    想要浏览heap dump,你可以使用 jhat(Java堆分析工具) 或者 MAT 读取生成的文件。
    
-finalizerinfo
    打印等待终结的对象信息。
    
-heap
    打印一个堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
    
-histo[:live]
    打印每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。
    打印的虚拟机内部的类名称将会带有一个'*'前缀。
    如果指定了live子选项,则只计算活动的对象。
    
-permstat
    打印Java堆内存的永久保存区域的类加载器的智能统计信息。
    对于每个类加载器而言,它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。
    此外,包含的字符串数量和大小也会被打印。
    
-F
    强制模式。如果指定的pid没有响应,请使用jmap -dump或jmap -histo选项。此模式下,不支持live子选项。
    
-h | -help
    打印帮助信息。
    
-J<flag>
    指定传递给运行jmap的JVM的参数。

5.4.2 例子

示例 1、jmap -histo <pid> 打印每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。

$ jmap -histo 14110

 num     #instances         #bytes  class name
----------------------------------------------
   1:         50994        7789848  [C
   2:         22160        6263680  [B
   3:          8527        1437176  [I
   4:         49099        1178376  java.lang.String
   5:            63         936248  [J
   6:         19118         611776  java.util.HashMap$Node
   7:          6243         549384  java.lang.reflect.Method
   8:          8948         509464  [Ljava.lang.Object;
   9:         10588         423520  java.util.TreeMap$Entry
  10:          3696         417952  java.lang.Class
  11:          1372         230544  [Ljava.util.HashMap$Node;
  12:          4760         228480  java.util.HashMap
  13:          4789         191560  java.util.HashMap$ValueIterator
  14:          5851         187232  java.io.File

B byte
C char
D double
F float
I int
J long
Z boolean
[ 数组,如[I表示int[]
[L+类名 其他对象

示例 2、jmap -heap <pid> 查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。

$ jmap -heap 14110

Attaching to process ID 14110, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 134217728 (128.0MB)
   NewSize                  = 44564480 (42.5MB)
   MaxNewSize               = 44564480 (42.5MB)
   OldSize                  = 89653248 (85.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 19398656 (18.5MB)
   used     = 6316256 (6.023651123046875MB)
   free     = 13082400 (12.476348876953125MB)
   32.56027634079392% used
From Space:
   capacity = 12582912 (12.0MB)
   used     = 98304 (0.09375MB)
   free     = 12484608 (11.90625MB)
   0.78125% used
To Space:
   capacity = 12582912 (12.0MB)
   used     = 0 (0.0MB)
   free     = 12582912 (12.0MB)
   0.0% used
PS Old Generation
   capacity = 89653248 (85.5MB)
   used     = 20189088 (19.253814697265625MB)
   free     = 69464160 (66.24618530273438MB)
   22.519081517269736% used

13984 interned Strings occupying 1890552 bytes.

示例 3、jmap -dump:format=b,file=<dumpFileName> <pid> 用jmap把进程内存使用情况dump到文件中

$ jmap -dump:format=b,file=/tmp/123.hprof 14110
Dumping heap to /tmp/123.hprof ...
Heap dump file created

5.5 jhat 工具

jhat 可以对 dump 出来的堆信息进行处理,以 html 页面的形式展示出来。

执行 jhat /tmp/123.hprof即可,默认端口是 7000,访问 http://localhost:7000 即可查看结果。通过 -port 指定端口。

Tomcat 基础优化

以上就是我整理出的 Tomcat 基础优化方法,如果有不对的地方或者你有更好的建议,欢迎在评论区留言,谢谢。

相关标签: 知识积累