一个服务的实际并发量收到很多方面因素的影响,大致归类一下如下:
1、数据库,这是web项目中最常见的瓶颈,解决方法一般都是通过cache
2、远程接口调用,解决方法是选择高性能的RPC框架,如dubbo+ZK等,使用长连接代替短连接
3、容器,容器本身的并发量是有上限的,所以大型系统都是分布式的
4、业务逻辑,复杂的业务逻辑肯定会花费更多的时间去处理,可以采用异步或多线程的方式解决
5.其他,如JVM调优,网络带宽,CDN加速等等很多其他因素
其中的容器就包括tomcat,tomcat是一个解压即可用的中间件,虽然默认配置可以使大多数正常运行,但是并不能发挥出服务最佳的性能。
本文主要记录对tomcat处理能力影响比较大的几个方面:
1、tomcat的运行模式,选对一个合适的运行模式会对tomcat的处理能力有质的影响
2、tomcat连接器中几个对并发量影响比较大的参数设置,类似超时时间,文件头大小等设置本篇不涉及。
一、tomcat连接器的几个关键参数
1、maxThreads
tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
该值的大小直接影响tomcat的并发处理能力
maxThreads如何配置
maxThreads="800"
一般的服务器操作都包括量方面:1计算(主要消耗cpu),2等待(io、数据库等)
第一种极端情况,如果我们的操作是纯粹的计算,那么系统响应时间的主要限制就是cpu的运算能力,此时maxThreads应该尽量设的小,降低同一时间内争抢cpu的线程个数,可以提高计算效率,提高系统的整体处理能力。
第二种极端情况,如果我们的操作纯粹是IO或者数据库,那么响应时间的主要限制就变为等待外部资源,此时maxThreads应该尽量设的大,这样 才能提高同时处理请求的个数,从而提高系统整体的处理能力。此情况下因为tomcat同时处理的请求量会比较大,所以需要关注一下tomcat的虚拟机内 存设置和linux的open file限制。
maxThreads的配置绝对不是越大越好
2、acceptCount
当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
Linux内核协议栈为一个tcp连接管理使用两个队列,一个是半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求),一个是全连接队列(accpetd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)。默认的话半连接队列的长度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默认是1024。如果开启了syncookies,那么基本上没有限制。
图片.png
tomcat有一个acceptor线程来accept socket连接,然后有工作线程来进行业务处理。对于client端的一个请求进来,在第三次握手之后,该连接进入到accept队列。tomcat的acceptor线程则负责从accept队列中取出该connection,接受该connection,然后交给工作线程去处理(读取请求参数、处理逻辑、返回响应等等;如果该连接不是keep alived的话,则关闭该连接,然后该工作线程释放回线程池,如果是keep alived的话,则等待下一个数据包的到来直到keepAliveTimeout,然后关闭该连接释放回线程池),然后自己接着去accept队列取connection(当当前socket连接超过maxConnections的时候,acceptor线程自己会阻塞等待,等连接降下去之后,才去处理accept队列的下一个连接)。acceptCount指的就是这个accept队列的大小
查看方法:
查看半连接队列:cat /proc/sys/net/core/somaxconn
查看半连接队列:cat /proc/sys/net/ipv4/tcp_max_syn_backlog
设置方法:
设置半连接队列大小:net.ipv4.tcp_max_syn_backlog = 16384
设置全连接队列大小:net.core.somaxconn = 16384
acceptCount的配置
acceptCount="1000"
这个值应该是主要根据应用的访问峰值与平均值来权衡配置的,通常可以使用默认值100或者设置为跟maxThreads相同
如果设的较小,可以保证接受的请求较快相应,但是超出的请求可能就直接被拒绝
如果设的较大,可能就会出现大量的请求超时的情况,因为我们系统的处理能力是一定的
3、maxConnections
这个值表示最多可以有多少个socket连接到tomcat上。NIO模式下默认是10000,对于BIO,默认的是maxThreads的值,APR /native的默认值是8192。
本参数与maxThreads是不同的,maxThreads是woker线程并发处理请求的最大数。也就是虽然client的socket连接上了,但是可能都在tomcat的task queue里头,等待worker线程处理返回响应。
maxConnections配置方法
maxConnections = "1000"
4、enableLookups
***如果你想request.getRemoteHost()的调用执行,以便返回的远程客户端的实际主机名的DNS查询,则设置为true。设置为false时跳过DNS查找,并返回字符串形式的IP地址(从而提高性能)。默认情况下,禁用DNS查找。
二、tomcat的几种HTTP连接器
*HTTP连接器即对应Connector中设置的protocol参数的值*
protocol:协议类型,可选类型有四种,分别为BIO(阻塞型IO),NIO,NIO2和APR。
1、 BIO:BIO(Blocking I/O),顾名思义,即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包及其子包)。Tomcat在默认情况下,是以bio模式运行的。遗憾的是,就一般而言,bio模式是三种运行模式中性能最低的一种。BIO配置采用默认即可。
protocol="HTTP/1.1"
2、 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"
3、 APR:APR(Apache Portable Runtime/Apache可移植运行时),是Apache HTTP服务器的支持库。你可以简单地理解为:Tomcat将以JNI的形式调用 Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高 Tomcat对静态文件的处理性能。
与配置 NIO运行模式一样,也需要将对应的 Connector节点的 protocol属性值改为:
protocol="org.apache.coyote.http11.Http11AprProtocol"
***注:在tomcat6和7版本中,服务器会自动选择使用BIO或者APR模式处理网络请求,但需要在本地安装tomcat-native,tomcat的bin目录下自带tomcat-native.tar.gz包。
*关于这几种模式的性能*
并不是说 BIO的性能就一定不如 NIO,这几种类型 Connector之间并没有明显的性能区别,它们之间实现流程和原理不同,所以它们的选择是需要根据应用的类型来决定的。
BIO更适合处理简单流程,如程序处理较快可以立即返回结果。简单项目及应用可以采用BIO。
NIO更适合后台需要耗时完成请求的操作,如程序接到了请求后需要比较耗时的处理这已请求,所以无法立即返回结果,这样如果采用BIO就会占用一个连接,而使用NIO后就可以将此连接转让给其他请求,直至程序处理完成返回为止。NIO是tomcat8上的默认运行模式。
APR可以大大提升Tomcat对静态文件的处理性能,同时如果你使用了HTTPS方式传输的话,也可以提升SSL的处理性能。
*附:apr安装方法*
tomcat7上设置BIO方式即使用protocol="HTTP/1.1"
时tomcat会自动搜索系统指定路径里是不是有apr的库文件,如果可以搜索到,则自动使用apr模式。默认tomcat安装时不安装apr库文件,apr需要自行安装并需要安装tomcat的bin目录下的tomcat-native包。
安装依赖库
因为apr模式本质是使用JNI技术调用操作系统IO接口,需要用到相关API的头文件
> yum install apr-devel
> yum install openssl-devel
> yum install gcc
> yum install make
gcc和make一般系统环境都会有,主要是安装apr-devel和openssl-devel(arp支持启用openssl的ssl功能,并且默认是开启的)
图片.png
依赖环境安装完之后安装apr的包(这里建议手动装,yum装之后后面安装tomcat-native包的时候会提示找不到apr的包,需要指定apr包的路径才行)
这里以apr-1.6.3为例
> tar jxvf apr-1.6.3.tar.bz2
> cd apr-1.6.3
> ./configure --prefix=/usr/local/apr
> make && make install
安装过apr之后还需要安装tomcat-native包
> cd tomcat-dufg/bin
> cd tomcat-native-1.1.31-src/jni/native
> ./configure --with-apr=/usr/local/apr
> make && make install
因为tomcat是需要搜索apr包的路径,所以和jdk一样apr也需要设置环境变量
*环境变量的三种设置方法*
1、设置LD_LIBRARY_PATH和LD_RUN_PATH环境变量,指向/usr/local/apr/lib目录,可配置到$HOME/.profile文件中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib
export LD_RUN_PATH=$LD_RUN_PATH:/usr/local/apr/lib
2、拷贝/usr/local/apr/lib目录下所有动态库到/usr/lib或/lib系统共享库搜索目录下即可。
> copy /usr/local/apr/lib/libtcnative* /usr/lib/
3、编辑$TOMCAT_HOME/bin/catalina.sh文件,在虚拟机启动参数JAVA_OPTS中添加java.library.path参数,指定apr库的路径(推荐使用这种方法)
JAVA_OPTS="$JAVA_OPTS -Djava.library.path=/usr/local/apr/lib"
设置好之后重启tomcat,查看catalina.out日志输出
图片.png
出现"http-apr-8080"和"ajp-apr-8009",说明已经apr已经成功启用。
未安装时的输出是类似:INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: xxx/xxx这种。
作者:进击的胖达
链接:https://www.jianshu.com/p/24338493c82f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
优化内存
/bin/catalina.sh添加JAVA_OPTS参数
jdk1.7
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:PermSize=1024m -XX:MaxPermSize=1024m -XX:+DisableExplicitGC"
jdk1.8
1.8版本中已经没有PermSize、MaxPermSize
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=1024M -XX:+DisableExplicitGC"
参数说明:
-Djava.awt.headless:没有设备、键盘或鼠标的模式。有关介绍:What is Headless mode in Java
-Dfile.encoding: 设置字符集
-server:jvm的server工作模式,对应的有client工作模式。使用“java -version”可以查看当前工作模式
-Xms512m:初始Heap大小,使用的最小内存
-Xmx1024m:Java heap最大值,使用的最大内存
-XX:NewSize=512m:表示新生代初始内存的大小,应该小于 -Xms的值
-XX:MaxNewSize=1024M:表示新生代可被分配的内存的最大上限,应该小于 -Xmx的值
-XX:PermSize=1024m:设定内存的永久保存区域(注:jdk1.8 was removed)
-XX:MaxPermSize=1024m:设定最大内存的永久保存区域(注:jdk1.8 was removed)
-XX:+DisableExplicitGC:自动将System.gc()调用转换成一个空操作,即应用中调用System.gc()会变成一个空操作
优化连接数
就算你把线程数调到很大,但是还是要看业务每个请求耗费的时间和资源
优化线程数
在conf/server.xml找到Connectorport=“8080” protocol=“HTTP/1.1”,增加maxThreads和acceptCount属性(使acceptCount大于等于maxThreads),如下:
<Connectorport="8080" protocol="HTTP/1.1"connectionTimeout="20000" redirectPort="8443"acceptCount="500" maxThreads="400" />
参数说明:
maxThreads:tomcat可用于请求处理的最大线程数,默认是200
minSpareThreads:tomcat初始线程数,即最小空闲线程数
maxSpareThreads:tomcat最大空闲线程数,超过的会被关闭
acceptCount:当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理.默认100
使用线程池
在conf/server.xml中增加executor节点,然后配置connector的executor属性,如下:
<Executorname="tomcatThreadPool" namePrefix="req-exec-"maxThreads="1000" minSpareThreads="50"maxIdleTime="60000"/>
<Connectorport="8080" protocol="HTTP/1.1"executor="tomcatThreadPool"/>
参数说明:
namePrefix:线程池中线程的命名前缀
maxThreads:线程池的最大线程数
minSpareThreads:线程池的最小空闲线程数
maxIdleTime:超过最小空闲线程数时,多的线程会等待这个时间长度,然后关闭
threadPriority:线程优先级
优化运行模式(connector for protocol)
-
BIO
一个线程处理一个请求。缺点:并发量高时,线程数较多,浪费资源。Tomcat7或以下在Linux系统中默认使用这种方式。
配置conf/server.xml
```
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" enableLookups="false" redirectPort="8443" URIEncoding="UTF-8" />```
-
NIO
利用Java的异步IO处理,可以通过少量的线程处理大量的请求。Tomcat8在Linux系统中默认使用这种方式。Tomcat7必须修改Connector配置来启动(conf/server.xml配置文件)。
配置conf/server.xml
3. apr
apr=Apache Portable Runtime,从操作系统层面解决io阻塞问题。
- 安装apr
下载apr: http://www.linuxfromscratch.org/blfs/view/svn/general/apr.html
```shell
tar xvf apr-1.5.2.tar.bz2
cd apr-1.5.2
./configure --prefix=/usr --disable-static --with-installbuilddir=/usr/share/apr-1/build &&
make
make install
```
> --disable-static: This switch prevents installation of static versions of the libraries
- 安装tomcat-native
进入tomcat/bin目录,比如:
```
cd /home/vhs/tomcat/bin
tar xzfv tomcat-native.tar.gz
cd tomcat-native-1.1.32-src/jni/native
./configure --with-apr=/usr/bin/apr-1-config
make
make install
```
- 配置server.xml
- 对tomcat设置环境变量,方法是在bin/catalina.sh文件中增加1行
` CATALINA_OPTS="-Djava.library.path=/usr/local/apr/lib" `
- 修改conf/server.xml
```
<Connector executor="tomcatThreadPool" port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol" connectionTimeout="20000" enableLookups="false" redirectPort="8443" URIEncoding="UTF-8" />
```
> tomcat启动遇到错误:failed to initialize the sslengine。
> 解决办法:
> 修改server.xml中Listener的className=“org.apache.catalina.core.AprLifecycleListener” 的参数SSLEngine为off。如下:`<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" />`
## 参考
- [Tomcat7优化配置](http://www.linuxprobe.com/tomcat7-configure-optimize.html)