本文从Tomcat配置层面,提高Tomcat服务器的并发性能。
Tomcat的配置优化主要有三方面,分为系统优化,Tomcat配置优化,Java 虚拟机(JVM)调优。
接下来将详细介绍Tomcat自身配置与JVM参数优化,以 Tomcat 7 为例。
0.1. Tomcat配置参数优化
0.1.1. Connector连接器并发优化: BIO/NIO/APR运行模式的选用
Tomcat的运行模式有3种:
BIO | Tomcat-7及以下版本默认模式 | 阻塞式I/O(blocking I/O),表示Tomcat使用的是传统Java I/O操作(java.io包及其子包)。由于每个请求都要创建一个线程来处理,线程开销较大,不能处理高并发的场景,在三种模式中性能也最低 |
NIO / NIO2(AIO) | Tomcat-8版本默认模式 | 即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)更好的并发运行性能。利用Java的异步IO处理,可以通过少量的线程处理大量的请求 |
APR | 即Apache Portable Runtime/Apache可移植运行时,是Apache HTTP服务器的支持库。APR从操作系统层面解决io阻塞问题,可以简单地理解为:Tomcat将以JNI的形式调用 Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高 Tomcat对静态文件的处理性能。Tomcat-APR也是在Tomcat上运行高并发应用的首选模式 |
0.1.1.1. 如何查看Tomcat当前以何种工作模式运行?
在Tomcat启动时,可以通过log文件(通常为catalina.out
文件)看到Connector使用的是哪一种运行模式:
如日志显示:
StartingProtocolHandler ["http-bio-8080"]
,即Tomcat正在以BIO模式运行;如日志显示:
StartingProtocolHandler ["http-nio-8080"]
,即Tomcat正在以NIO模式运行;如日志显示:
StartingProtocolHandler ["http-apr-8080"]
,即Tomcat正在以APR模式运行;
0.1.1.2. protocol-BIO模式的启用(需重启Tomcat生效)
Tomcat-7及更低版本默认使用BIO模式。如需由其他模式更改为BIO模式,则修改server.xml
文件Connector-protocol属性,设置为protocol="HTTP/1.1"
即可,如图示:
0.1.1.3. protocol-NIO模式的启用(需重启Tomcat生效)
Tomcat-8及更高版本默认使用NIO模式。如使用较低版本或由其他模式更改为NIO模式,同样修改server.xml
文件Connector-protocol属性,设置为protocol="org.apache.coyote.http11.Http11NioProtocol"
即可,如图示:
0.1.1.4. protocol-APR模式的启用(需重启Tomcat生效)
启用这种模式稍微麻烦一些,需要预先安装一些依赖库。
0.1.1.4.1. 安装APR/APR-iconv和APR-util
0.1.1.4.1.1. 安装包的获取
进入APR-官网选择适宜版本的安装包,下载并解压至系统路径:
cd /usr/local/src/
wget http://mirror.bit.edu.cn/apache//apr/apr-1.6.5.tar.gz
tar -xvf apr-1.6.5.tar.gz
wget http://mirror.bit.edu.cn/apache//apr/apr-util-1.6.1.tar.gz
tar -xvf apr-util-1.6.1.tar.gz
wget http://mirror.bit.edu.cn/apache//apr/apr-iconv-1.2.2.tar.gz
tar -xvf apr-iconv-1.2.2.tar.gz
0.1.1.4.1.2. 安装apr
cd /usr/local/src/apr-1.6.5/
./configure --prefix=/usr/local/APR-1.6.5
make
make install
【注】 configure这一步可能会报
rm: cannot remove 'libtoolT': No such file or directory config.status: executing default commands
错误。出现这个错误时,需修改configure文件,将$RM "$cfgfile"
改为$RM -f "$cfgfile"
,然后重新编译一次即可。
0.1.1.4.1.3. 安装apr-iconv
cd /usr/local/src/apr-iconv-1.2.2/
./configure --prefix=/usr/local/APR-ICONV-1.2.2 --with-apr=/usr/local/APR-1.6.5
make
make install
0.1.1.4.1.4. 安装apr-util
cd /usr/local/src/apr-util-1.6.1/
./configure --prefix=/usr/local/APR-UTIL-1.6.1 --with-apr=/usr/local/APR-1.6.5 --with-apr-iconv=/usr/local/APR-ICONV-1.2.2/bin/apriconv
make
make install
【注】 如make这一步报错
xml/apr_xml.c:35:19: fatal error: expat.h: No such file or directory compilation terminated
,说明系统缺少expat库,预先安装expat库即可:apt-get install libexpat1-dev
或yum install expat-deve -y
。
0.1.1.4.2. 安装tomcat-native
在tomcat/bin/
路径下有相应的安装tomcat-native的tar包,解压并安装至选定的路径即可:
cd /usr/local/Tomcat_Server/tomcat_server_colony/apache-tomcat-7.0.90_0/bin/
tar -xvf tomcat-native.tar.gz -C /usr/local/Tomcat_Server/tomcat-native/
cd /usr/local/Tomcat_Server/tomcat-native/tomcat-native-1.2.17-src/native/
./configure --with-apr=/usr/local/APR-1.6.5 --with-java-home=/usr/local/Java/jdk1.8.0_131
make
make install
【注】如系统同时安装多个tomcat副本,则安装一次tomcat-native即可。
0.1.1.4.3. 配置APR环境变量
编辑/etc/profile
,在文件末尾追加如下路径:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/APR-1.6.5/lib
export LD_RUN_PATH=$LD_RUN_PATH:/usr/local/APR-1.6.5/lib
保存后,输入source /etc/profile
命令,使改动立即生效。
如果tomcat-native并未安装在Tomcat内部路径下,而是安装至自定义路径,则需进一步修改tomcat/bin目录下setclasspath.sh
文件,在其他命令之前加入LD_LIBRARY_PATH
变量:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<tomcat-native-libs路径>
export LD_LIBRARY_PATH
如:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/Tomcat_Server/tomcat-native/tomcat-native-1.2.17-src/native/.libs
export LD_LIBRARY_PATH
0.1.1.4.4. 启用protocol-APR模式
修改server.xml
文件Connector-protocol属性,设置为protocol="org.apache.coyote.http11.Http11AprProtocol"
即可,如图示:
0.1.2. Server配置参数(缓存/协议类型)优化(需重启Tomcat生效)
0.1.2.1. 禁用AJP连接器
AJP(Apache JServerProtocol)是为 Tomcat 与 HTTP 服务器之间通信而定制的协议,能提供较高的通信速度和效率。
如果tomcat前端放的是apache的时候,会使用到AJP这个连接器。
使用Nginx+tomcat的架构时,用不到AJP协议,所以在这里可以把AJP连接器禁用。
编辑server.xml
文件,将AJP相关配置的代码注释掉即可:
< ! -- <Connector port="8019" protocol="AJP/1.3" redirectPort="8443" /> - - >
0.1.2.2. 启用线程池
默认的tomcat没有启用线程池。
在tomcat中每一个用户请求都是一个线程,所以启用线程池可以使用线程池提高性能。这里前台其实有一个调度线程,然后调度线程会放入线程池内,然后到到一定的时候线程池的任务变成工作线程。
具体配置方式如下:
- 打开
server.xml
文件,取消<Executor
代码段的注释。
为Connector指定线程池。
executor="tomcatThreadPool"
- 调整Executor参数,如:
< Executor
name=”tomcatThreadPool”
minSpareThreads=”4”
maxThreads=”500”
namePrefix=”catalina-exec-“
/ >
0.1.2.3. tomcat-Connector并发处理/缓存参数优化
maxThreads | 客户请求最大线程数 |
minSpareThreads | Tomcat初始化时创建的 socket 线程数 |
maxSpareThreads | Tomcat连接器的最大空闲 socket 线程数 |
enableLookups | 是否反查域名,取值为: true 或 false 。为了提高处理能力,应设置为 false |
redirectPort | 在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口 |
acceptAccount | 监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads ) |
connectionTimeout | 连接超时 |
minProcessors | 服务器创建时的最小处理线程数 |
maxProcessors | 服务器同时最大处理线程数 |
URIEncoding | URL统一编码 |
【注】其中和最大连接数相关的参数为maxProcessors 和 acceptCount 。如果要加大并发连接数,应同时加大这两个参数。
示例:
< Connector port=”8090”
minSpareThreads=”100”
maxThreads=”1000”
URIEncoding=”UTF-8”
enableLookups=”false”
disableUploadTimeout=”true”
tcpNoDelay=”true”
maxConnections=”10000”
acceptorThreadCount=”2”
acceptCount=”1000”
redirectPort=”8443”
connectionTimeout=”20000”
protocol=”org.apache.coyote.http11.Http11AprProtocol”
executor=”tomcatThreadPool”
/ >
0.1.2.4. tomcat-Connector压缩参数优化
compression | compression="on";打开压缩功能 |
compressionMinSize | 启用压缩的输出内容大小,默认为2KB |
noCompressionUserAgents | 对于指定的浏览器,不启用压缩 |
compressableMimeType | 针对哪些资源类型需要压缩 |
【注】 Tomcat 的压缩是在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览。
相对于普通的浏览过程 HTML、CSS、Javascript和Text,它可以节省40% 左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP、JSP、ASP、Servlet,SHTML等输出的网页也能进行压缩,压缩效率也很高。
但是压缩也会增加 Tomcat 的负担,因此最好采用Nginx + Tomcat 或者 Apache + Tomcat 方式,将压缩的任务交由 Nginx/Apache 去做。
示例:
< Connector port=”8090”
compressableMimeType=”text/html,text/xml,text/css,text/javascript,text/plain,application/javascript,text/json”
noCompressionUserAgents=”gozilla, traviata”
compressionMinSize=”2048”
compression=”on”
/ >
0.1.2.5. tomcat-Host项目路径调整
如果tomcat以集群的形式作为应用容器,在项目更新时逐一向集群中各个tomcat容器的webapps路径上传项目war包文件,不仅耗时,更占用系统的可用资源。
如将集群中的tomcat容器项目路径指向同一个指定目录下,容器中项目的维护和储存会更加简洁高效。
具体配置为:
编辑server.xml
文件,修改Host-appBase
属性至选定自定义路径,如:
< Host name=”localhost”
appBase=”/usr/local/Tomcat_Server/tomcat_server_colony/colony_webapps/“
unpackWARs=”true”
autoDeploy=”true”
0.1.2.6. log日志配置优化
在默认的配置下,tomcat运行期间会输出一大堆的日志。而这些日志对日常运维来说,基本没有帮助,反而徒增很多无用信息和I/O负担。
如能简化日志的写入配置,也能为tomcat增速不少。
Tomcat日志相关的优化主要包含如下方面:
0.1.2.6.1. 关闭localhost_access_log日志
修改在tomcat的安装目录conf文件夹下server.xml里配置,将AccessLogValue注释掉。
< ! - - Valve className="org.apache.catalina.valves.AccessLogValue" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" / - - >
0.1.2.6.2. 调整logging.properties设置,屏蔽非常用日志
修改conf/logging.properties
日志配置文件可以屏蔽掉部分的日志信息。
将level级别设置成WARNING
就可以大量减少日志的输出,当然也可以设置成OFF
,直接禁用掉。
与此类似,也可以观察下tomcat容器内的项目日志级别,部署时尽量设置为
OFF
或ERROR
或WARNING
等级。
示例配置如下:
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
#.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
1catalina.org.apache.juli.FileHandler.level = OFF
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.
#2localhost.org.apache.juli.FileHandler.level = FINE
#2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#2localhost.org.apache.juli.FileHandler.prefix = localhost.
#3manager.org.apache.juli.FileHandler.level = FINE
#3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#3manager.org.apache.juli.FileHandler.prefix = manager.
#4host-manager.org.apache.juli.FileHandler.level = FINE
#4host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#4host-manager.org.apache.juli.FileHandler.prefix = host-manager.
java.util.logging.ConsoleHandler.level = OFF
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = OFF
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = OFF
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = OFF
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler
# For example, set the org.apache.catalina.util.LifecycleBase logger to log
# each component that extends LifecycleBase changing state:
#org.apache.catalina.util.LifecycleBase.level = FINE
# To see debug messages in TldLocationsCache, uncomment the following line:
#org.apache.jasper.compiler.TldLocationsCache.level = FINE
0.1.2.6.3. 定期清理或切割压缩catalina.out文件
catalina.out
文件过大会造成日志写入速度慢,会间接拖慢tomcat的响应速度。
为了避免文件过大造成的I/O速度慢,可以通过定期清空或拆分catalina.out
文件来降低catalina.out
文件体积。这里比较推荐通过crontab
自动执行脚本来实现。
清空
catalina.out
文件cat /dev/null > <catalina.out文件路径> ;
按日期切割压缩打包日志(tar.gz)
#!/bin/bash DIR=$(cd $(dirname ${BASH_SOURCE[0]}); pwd ) echo $DIR/../logs d=`date +%Y%m%d%H` cd $DIR/../logs cp catalina.out catalina.out.${d} echo "" > catalina.out tar -czvf catalina.out.${d}.tar.gz catalina.out.${d} rm -rf catalina.out.${d} find $DIR/../logs -type f -mtime +15 -name "*.log" -exec rm -f {} \; find $DIR/../logs -type f -mtime +15 -name "*.tar.gz" -exec rm -f {} \;
0.1.2.6.4. 关闭catalina.out文件(不推荐)
catalina.out
文件是tomcat的启动日志,很多时候可以帮助我们运维,所以不建议完全关闭,如确实过大可以用上一个方式定期缩减此文件的体积。
如确实需要关闭,可以设置bin/catalina.sh
文件。找到if [ -z "$CATALINA_OUT" ] ; then
代码段,改写成:
if [ -z "$CATALINA_OUT" ] ; then
CATALINA_OUT="$CATALINA_BASE"/logs/catalina.out
fi
0.2. JVM(Tomcat启动参数/内存)优化(需重启Tomcat生效)
在tomcat的启动脚本catalina.sh
中设置JAVA_OPTS
参数。
-server | 启用jdk 的 server 版 |
-Xms | java虚拟机初始化时的最小内存 |
-Xmx | java虚拟机可使用的最大内存 |
-XX: PermSize | 内存永久保留区域 |
-XX:MaxPermSize | 内存最大永久保留区域 |
示例:
JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms1024m -Xmx2048m -XX:NewSize=512m -XX:MaxNewSize=1024m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=356m -XX:NewRatio=2 -XX:MaxTenuringThreshold=12 -XX:+DisableExplicitGC"
0.3. 系统参数(网卡驱动)优化
优化网卡驱动可以有效提升性能,这个对于集群环境工作的时候尤为重要。
通常线上项目会采用Linux服务器,所以优化内核参数也是一个非常重要的工作。
现给一个参考的优化参数方案:
修改
/etc/sysctl.cnf
文件,在文件末尾追加如下配置:net.core.netdev_max_backlog = 32768 net.core.somaxconn = 32768 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.route.gc_timeout = 100 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 65536
保存退出,执行
sysctl -p
命令,使上一步配置立即生效。