一个服务的实际并发量收到很多方面因素的影响,大致归类一下如下:
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,那么基本上没有限制。
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功能,并且默认是开启的)
依赖环境安装完之后安装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日志输出
出现"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这种。
更多tomcat配置项请参考:http://blog.sina.com.cn/s/blog_64a52f2a0101g3sq.html