tomcat的运作和性能调优杂谈

对于很多刚学习java web的同学,tomcat这个web服务器肯定不陌生。我也是在自己学了两年web后,才真正去了解一下它。以前看过不少tomcat 的性能调优,可都是些技术指标,需要硬记,所以没多久都忘记了。这两天看了一些tomcat内部工作原理的书籍,再配合性能调优的一些资料,才在大脑中算是记忆扎根。这里也推荐各位先去看看tomcat内部机制,再谈调优,才能有更好的认知和更长的记忆。

tomcat的运作

网络通信

其实不管是servlet容器还是im通信框架,在java里面,到底都是一个个socket连接、处理、释放,这一点虽然很普通,但是很多人初学者并没有注意到,可能是太过于底层,而tomcat一直都是以web服务器这样一个伟大的工程展现出来,初学者可能会在两者之间很难联系上。在这里,可能会涉及到TCP三次握手、操作系统层面的请求队列、java bio、nio等。

运作基本思路

我们从最简单的java传统io的方式来开始讲起,服务端自然要有一个serverSocket的实例,负责来自客户端的连接,accept阻塞后获得一个socke实例,这个socket其实就是一个connection。接着,读取数据报第一行的请求方法method,请求路径path,使用协议和版本以及数据报中header部分,将其封装成一个httpRequest和httpResponse。从请求路径中解析出要指定到的servlet名字,如果是静态资源的话,就根据目录获取到相应的静态文件并返回,如果是动态的,通过类加载器urlclassLoader加载用户自定义的servlet实例,然后再调用用户自定义的service方法。最后,关闭socket,结束连接。这个机制虽然有很多瑕疵,但是是整个tomcat运行的核心流程。

连接器connector和容器container

上述关于tomcat运作机制的描述,基本可以分为两个部分——连接的获取connector和请求的处理container。tomcat中,connector主要分为两个对象,即一个单线程负责客户端连接的connector和多线程处理httpProcessor。前者将连接封装成一个个socket,后者是一个对象池(也是线程池),每个线程将socket分配一个processor线程,负责封装req和resp以及调用container的invoke方法。container是一个实际的servlet容器,里面都是用户自定义的servlet,tomcat使用的容器成为catalina,而使用的connector为coyote。容器根据作用域可以分为4中模型,即engine、host、context、wrapper。主要说一下后面两个,wrapper可以简单理解为单个servelt,而context是一个java web项目。container中还涉及到管道pipeline和valve相关的内容,管道相当于调用servlet的一个流水线,valve是其流水线上的阀门,可以理解为一个个task,比如日志valve、记录http头部valve,还有最基本的valve,这个是内置且必须的,在管道调用的最后一个,这个task,就是调用servlet的service方法。

性能调优

tomcat三种协议模式

tomcat的http有三种协议模式,即bio、nio和apr。前两者很好理解,后面这个,是win os默认下的模式,这个可以简单理解为native,调用操作系统底层的c++函数库,在静态资源访问很大的网站上建议使用,效率最高。默认是bio,这种阻塞型效率往往很差,建议设置成nio。

调优参数

maxThread、maxConnection和acceptCount
前两个参数,对于网上很多人都有一些的说法,但是都没有说清具体的区别,因为在bio上,默认根本就没啥区别,但是在其他模式下不是这样。

这里写图片描述

前面说到,serversocket能获取到多少个socket,其实对应的就是maxConnetction。也就是在tomcat服务的端口上,对应上图中accept函数能够同时接收到的多少个socket,在没有处理线程的极端条件下,就是图中socket队列的最大长度。而maxThread其实是操作5,tomcat能同时启动多少个处理线程来处理已经在队列中的socket们。一般tomcat建议是使用线程池exector来控制线程数,一旦设置了线程池,maxThread就无效了。在bio默认下,maxThread和maxConnection是一样的,但是在nio上,maxConnction默认是10000,arp将会有更大值。处理线程数量会受到操作系统的限制,默认是200。
上图中,在操作系统os层面上,有两个队列,sync队列和accept队列。前者是tcp三次握手尚未完成时候的队列,当第三次握手信号ack发送到服务器时,连接就会冲sync转移到accept队列。而acceptCount,实际上,就是这个accept队列的长度。当accpt函数已经达到了maxConnetion设定的最大连接数后,意味着accpt不在获取新的socket,而是一直处于一个阻塞状态,直到连接数小于maxConnection。打个比喻,这时候的accept相当于在门口排队的人,而被accept函数获取的连接是进了门的人。当排队的数量达到了acceptCount后,意味着后面再有连接想排队都不给了(有点绝情),直接拒绝服务。

minSpareThread
上述提到tomcat的线程池maxThread,当未达到这个数值的时候,有新的socket进来,就会一直创建一个新的线程去处理。但是如果还没有任何请求的时候,线程池里面就没有线程了?当然不是,minSpareThread就是在没有请求时候,最少要保持住的最小线程数。

ConnectionTimeout和keepAliveTimeout
这两个超时时间也偶尔会让人有误解。前者是连接已经被accept获取后,但是长时间(可能才几十秒)没有收到客户端发送的fin或者数据传送过来,已经是占着茅坑不拉屎,后面还有很多人排队呢!当然要超时T出去。
keepAliveTimeout是http1.1设定的属性同一个连接,可以反复使用几次请求的情况下使用的。当第一次请求数据包发送完后,第二次数据包在这个时间段内没有到来,就out了,也可以理解为请求后多久才关闭连接,相当于给一个订单量特别大的客户服务,只要两次购物之间不超过这个时间,就给你永远保持绿色通道。但也意味着,普通连接都要等这么久,才会关闭掉。

猜你喜欢

转载自blog.csdn.net/jerryJavaCoding/article/details/78546063