Tomcat性能调优 | 示例
一、Tomcat工作原理
1. TCP的三次握手四次挥手
三次握手:
说明:类比于A和B打电话:
A对B说:你好,我是A,你能听到我说话吗?
B对A说:嗯,我能听到你说话
A对B说:好,那我们开始聊天吧
在服务器上使用如下命令能看到当前服务器的连接情况
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
返回结果说明:
LAST_ACK 5 (正在等待处理的请求数)
SYN_RECV 30
ESTABLISHED 1597 (正常数据传输状态)
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057 (处理完毕,等待超时结束的请求数)
四次挥手:
说明:
同样用A和B打电话来说明:
A对B说:我说完了,我要挂电话了
B对A说:等一下,我还没说完
B继续对A说:我说完了,你可以挂电话了
A对B说:好,我挂电话了
其他参数说明:
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉
2. Tomcat内部结构
上图说明:
•server:指的是整个应用的上下文, 也是最顶层的容器,tomcat中所有的东西都在这个server里边。
•service:指的是一个服务,主要的功能是把connector组件和engine组织起来,使得通过connector组件与整个容器通讯的应用可以使用engine提供的服务。
•engine:服务引擎,这个可以理解为一个真正的服务器,内部提供了多个虚拟主机对外服务。
•host:虚拟主机,每一个虚拟主机相当于一台服务器,并且内部可以部署多个应用,每个虚拟主机可以绑定一个域名,并指定多个别名。
•context:应用上下文,每一个webapp都有一个单独的context,也可以理解为每一个context代表一个webapp。
•connector:连接器组件,可以配置多个连接器支持多种协议,如http,APJ 等
组件说明:
Tomcat常见组件:
•服务器(server):Tomcat的一个实例,通常一个JVM只能包含一个Tomcat实例;因此,一台物理服务器上可以在启动多个JVM的情况下在每一个JVM中启动一个Tomcat实例,每个实例分属于一个独立的管理端口。这是一个*组件。
•服务(service):一个服务组件通常包含一个引擎和与此引擎相关联的一个或多个连接器。给服务命名可以方便管理员在日志文件中识别不同服务产生的日志。一个server可以包含多个service组件,但通常情下只为一个service指派一个server。
连接器类组件:
•连接器(connectors):负责连接客户端(可以是浏览器或Web服务器)请求至Servlet容器内的Web应用程序,通常指的是接收客户发来请求的位置及服务器端分配的端口。默认端口通常是HTTP协议的8080,管理员也可以根据自己的需要改变此端口。还可以支持HTTPS ,默认HTTPS端口为8443。同时也支持AJP,即(A)一个引擎可以配置多个连接器,但这些连接器必须使用不同的端口。默认的连接器是基于HTTP/1.1的Coyote。同时,Tomcat也支持AJP、JServ和JK2连接器。
容器类组件:
•引擎(Engine):引擎通是指处理请求的Servlet引擎组件,即Catalina Servlet引擎,它检查每一个请求的HTTP首部信息以辨别此请求应该发往哪个host或context,并将请求处理后的结果返回的相应的客户端。严格意义上来说,容器不必非得通过引擎来实现,它也可以是只是一个容器。如果Tomcat被配置成为独立服务器,默认引擎就是已经定义好的引擎。而如果Tomcat被配置为Apache Web服务器的提供Servlet功能的后端,默认引擎将被忽略,因为Web服务器自身就能确定将用户请求发往何处。一个引擎可以包含多个host组件。
•主机(Host):主机组件类似于Apache中的虚拟主机,但在Tomcat中只支持基于FQDN的“虚拟主机”。一个引擎至少要包含一个主机组件。
•上下文(Context):Context组件是最内层次的组件,它表示Web应用程序本身。配置一个Context最主要的是指定Web应用程序的根目录,以便Servlet容器能够将用户请求发往正确的位置。Context组件也可包含自定义的错误页,以实现在用户访问发生错误时提供友好的提示信息。
被嵌套类(nested)组件:
这类组件通常包含于容器类组件中以提供具有管理功能的服务,它们不能包含其它组件,但有些却可以由不同层次的容器各自配置。
•阀门(Valve):用来拦截请求并在将其转至目标之前进行某种处理操作,类似于Servlet规范中定义的过滤器。Valve可以定义在任何容器类的组件中。Valve常被用来记录客户端请求、客户端IP地址和服务器等信息,这种处理技术通常被称作请求转储(request dumping)。请求转储valve记录请求客户端请求数据包中的HTTP首部信息和cookie信息文件中,响应转储valve则记录响应数据包首部信息和cookie信息至文件中。
•日志记录器(Logger):用于记录组件内部的状态信息,可被用于除Context之外的任何容器中。日志记录的功能可被继承,因此,一个引擎级别的Logger将会记录引擎内部所有组件相关的信息,除非某内部组件定义了自己的Logger组件。
•领域(Realm):用于用户的认证和授权;在配置一个应用程序时,管理员可以为每个资源或资源组定义角色及权限,而这些访问控制功能的生效需要通过Realm来实现。Realm的认证可以基于文本文件、数据库表、LDAP服务等来实现。Realm的效用会遍及整个引擎或*容器,因此,一个容器内的所有应用程序将共享用户资源。同时,Realm可以被其所在组件的子组件继承,也可以被子组件中定义的Realm所覆盖。
二、优化思路
1. 网络优化
BIO、NIO、NIO2、APR,也就是阻塞与非阻塞
压缩gzip、超时配置,防止close_wait过多。
1.1、非阻塞,Tomcat8已经取消BIO
四种请求连接模型
HTTP/1.1
org.apache.coyote.http11.Http11Protocol 阻塞模式的连接协议
org.apache.coyote.http11.Http11NioProtocol 非阻塞模式的连接协议
org.apache.coyote.http11.Http11Nio2Protocol 非阻塞模式的连接协议
org.apache.coyote.http11.Http11AprProtocol – 本地连接协议
1.2、启用压缩,消耗CPU,减小网络传输大小
compression="on"
disableUploadTimeout="true"
compressionMinSize="2048"
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,a
pplication/javascript"
URIEncoding="utf-8"
2. 并发优化
最大线程数
最佳并发数。。。
连接数:maxConnections(最大连接数)
处理线程:maxThreads(操作系统允许多少线程,线程多大会引起切换效能)
等候队列:acceptCount(排队数量)指最大连接数已经满了的时候允许多少请求排队
3. 底层优化
JVM优化
多实例(必须的)
操作系统优化
JVM优化:固定堆内存,多线程并发收集,对象预留新生代,大对象进入老年代,启用内联
多实例:多个tomcat实例在一台机上
操作系统优化:网络参数,线程数,关闭IPV6,最大文件数
Linux服务器每进程不允许超过1000个线程,据说6、700线程服务器切换线程就慢下来
命令:ps -eLf | grep java | wc –l 可以查看当前启动的java进程里面有多少个线程
Linux线程栈大小是8M,可以使用ulimit –s设置
三、优化实战
内存优化
1. 优化tomcat/conf配置文件
/etc/tomcat/tomcat/conf文件修改JAVA_OPTS
JAVA_OPTS=“-server –Xmx2048m–Xms2048m –Xmn768m - XX:TargetSurvivorRatio=90 -XX:PetenureSizeThreshold=1000000 - XX:MaxTenuringThreshold=30 –XX:+UseParallelGC
–XX:+UseConcMarkSweepGC –XX:ParallelGCThreads=2"
2. 优化server.conf配置文件
/etc/tomcat/server.conf文件修改配置
-server:启用 JDK的 server 版本;-Xms:Java虚拟机初始化时堆的最小内存,一般与 Xmx配置为相同值,这样的好处是GC不必再为扩展内存空间而消耗性能;-Xmx:Java虚拟机可使用堆的最大内存;-XX:PermSize:Java虚拟机永久代大小;-XX:MaxPermSize:Java虚拟机永久代大小最大值;
-
Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" //最大并发数,默认设置 200,一般建议在 500 ~ 800,根据硬件设施和业务来判断
-
minSpareThreads="100" //Tomcat 初始化时创建的线程数,默认设置 25
-
prestartminSpareThreads = "true"//在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,如果不等于 true,minSpareThreads 的值就无效
-
maxQueueSize = "100"//最大的等待队列数,超过则拒绝请求 />
-
<Connector executor="tomcatThreadPool" port="8080"
-
protocol="org.apache.coyote.http11.Http11Nio2Protocol" //Tomcat 8 设置 nio2 更好,Tomcat 6 、7设置nio更好:org.apache.coyote.http11.Http11NioProtocol
-
connectionTimeout="20000"
-
minSpareThreads="100" maxSpareThreads="1000"最大处理连接数线程
-
minProcessors="100“同时处理请求的最小数
-
maxProcessors=“1000”同时处理请求的最大数
-
maxConnections="1000" redirectPort="8443"
-
enableLookups="false" //禁用DNS查询 acceptCount="100" //指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,默认设置 100
-
maxPostSize="10485760" //以 FORM URL 参数方式的 POST 提交方式,限制提交最大的大小,默认是2097152(2兆),它使用的单位是字节。10485760 为 10M。如果要禁用限制,则可以设置为 -1。
-
compression="on" disableUploadTimeout="true" compressionMinSize="2048"
-
acceptorThreadCount="2" //用于接收连接的线程的数量,默认值是1。一般这个指需要改动的时候是因为该服务器是一个多核CPU,如果是多核CPU一般配置为 2.
-
compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/ja
-
vascript" URIEncoding="utf-8" keepAliveTimeout="0"
-
关闭shutdown端口:<Server port="-1" shutdown="SHUTDOWN">
-
关闭ajp连接:注释<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-
取消访问日志Valve阀门
-
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
-
prefix="localhost_access_log." suffix=".txt"
-
pattern="%h %l %u %t "%r" %s %b" />
-
/>
图示:
配置优化
我们知道 TOMCAT_HOME/conf/server.xml 可以配置端口,虚拟路径等等 Tomcat 相关主要配置。
1) Connector 优化
Connector 是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector 的优化是重要部分。默认情况下 Tomcat 只支持 200 线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。
修改这部分配置需要修改 TOMCAT_HOME/conf/server.xml,打开 server.xml 找到 Connector 标签项,默认配置如下:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
其中 port 代表服务接口;
protocol 代表协议类型;
connectionTimeout 代表连接超时时间,单位为毫秒;
redirectPort 代表安全通信(https)转发端口,一般配置成 443。
可以看到除了这几个基本配置外并无特殊功能,所以我们需要对 Connector 进行扩展。
其中 Connector 支持参数属性可以参考 Tomcat 官方网站(https://tomcat.apache.org/tomcat-8.0-doc/config/http.html ),非常多,所以本文就只介绍些常用的。
我们将 Connector 配置修改为如下:
<Connector port="8080"
protocol="HTTP/1.1"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
connectionTimeout="20000"
maxHttpHeaderSize="8192"
tcpNoDelay="true"
redirectPort="8443"
enableLookups="false"
URIEncoding="UTF-8" />
-
port:代表 Tomcat 监听端口,也就是网站的访问端口,默认为 8080,可以根据需要改成其他。
-
-protocol:协议类型,可选类型有四种,分别为 BIO(阻塞型 IO),NIO,NIO2 和 APR。
BIO:BIO(Blocking I/O),顾名思义,即阻塞式 I/O 操作,表示 Tomcat 使用的是传统的 Java I/O 操作 (即 java.io 包及其子包)。Tomcat 在默认情况下,是以 bio 模式运行的。遗憾的是,就一般而言,bio 模式是三种运行模式中性能最低的一种。BIO 配置采用默认即可。NIO:NIO(New I/O),是 Java SE 1.4 及后续版本提供的一种新的 I/O 操作方式 (即 java.nio 包及其子包)。Java nio 是一个基于缓冲区、并能提供非阻塞 I/O 操作的 Java API,因此 nio 也被看成是 non-blocking I/O 的缩写。它拥有比传统 I/O 操作 (bio) 更好的并发运行性能。要让 Tomcat 以 nio 模式来运行也比较简单,我们只需要 protocol 类型修改为:
//NIO
protocol="org.apache.coyote.http11.Http11NioProtocol"
//NIO2
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
即可
2) 线程池
Executor 代表了一个线程池,可以在 Tomcat 组件之间共享。使用线程池的好处在于减少了创建销毁线程的相关消耗,而且可以提高线程的使用效率。
要想使用线程池,首先需要在 Service 标签中配置 Executor,如下:
<Service name="Catalina"> <Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="1000"
minSpareThreads="100"
maxIdleTime="60000"
maxQueueSize="Integer.MAX_VALUE"
prestartminSpareThreads="false"
threadPriority="5"
className="org.apache.catalina.core.StandardThreadExecutor"/>
....
其中,name:线程池名称,用于 Connector中指定。namePrefix:所创建的每个线程的名称前缀,一个单独的线程名称为 namePrefix+threadNumber。maxThreads:池中最大线程数。minSpareThreads:活跃线程数,也就是核心池线程数,这些线程不会被销毁,会一直存在。maxIdleTime:线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为6000(1分钟),单位毫秒。maxQueueSize:在被执行前最大线程排队数目,默认为Int的最大值,也就是广义的无限。除非特殊情况,这个值不需要更改,否则会有请求不会被处理的情况发生。prestartminSpareThreads:启动线程池时是否启动 minSpareThreads部分线程。默认值为false,即不启动。threadPriority:线程池中线程优先级,默认值为5,值从1到10。className:线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。如果想使用自定义线程池首先需要实现 org.apache.catalina.Executor接口。
线程池配置完成后需要在 Connector 中指定:
<Connector executor="tomcatThreadPool" ...
3) Listener 配置
另一个影响 Tomcat 性能的因素是内存泄露。Server 标签中可以配置多个 Listener,其中 JreMemoryLeakPreventionListener 是用来预防 JRE 内存泄漏。此 Listener 只需在 Server 标签中配置即可,默认情况下无需配置,已经添加在 Server 中。
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
缓存优化
参数说明
compression 打开压缩功能
compressionMinSize 启用压缩的输出内容大小,这里面默认为2KB
compressableMimeType 压缩类型
connectionTimeout 定义建立客户连接超时的时间. 如果为 -1, 表示不限制建立客户连接的时间
<Connector port="9027"...........
compression="on" compressionMinSize="2048"connectionTimeout="20000"compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"................
/>
大家通过以上我们队 tomcat 的有效配置,包括 (从内存,运行模式、并发、缓存 4 个方面) 优化。能让自己的项目稳定的服务于客户,达到我们理想的运行效果。
想了解或学习更多关于性能测试放面的课程,可加我微信
我们将拉你进专业性能知识群!!!资料免费哦