1. tomcat 7的一些关键参数配置
官方文档 http://tomcat.apache.org/tomcat-7.0-doc/config/http.html
配置文件conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
没有指定Executor,则使用internal executor,使用的是jdk的线程池。本文讨论internal executor。
maxThreads – 默认200,最大线程数,为线程池最大大小(ThreadPoolExecutor的maximumPoolSize)
maxConnections – 最大连接数,默认为maxThreads
acceptCount – 默认100,监听socket的 backlog。
minSpareThreads – 默认min(10,maxThreads) ,等同于ThreadPoolExecutor的corePoolSize
maxKeepAliveRequests – 默认100,一条长连接服务的http请求最大数目(超过了关闭之),-1表示不受数目影响。
connectionTimeout – 默认60000ms,accept一个连接后,等URI的最长时间。
keepAliveTimeout – 默认为connectionTimeout,一个连接等待下一个http请求到来的最长时间(过期关闭该连接)
2. 主要线程
2.1 http监听线程
代码为:JIoEndpoint$Acceptor.run\
线程名为:http-bio-监听端口-Acceptor-0
关键逻辑
public void run() {
while (running) {
// 连接数到了maxConnections则等待,否则计数器加1
countUpOrAwaitConnection();
Socket socket = accept();
封装socket为runnable对象(JIoEndpoint$SocketProcessor对象)提交给线程池;
}
}
注: 线程池使用的队列为TaskQueue extends LinkedBlockingQueue,其重载了offer方法。所以线程池的行为与JDK里的稍有不同:连接数小于minSpareThreads,来一个连接建个线程;minSpareThreads<=连接数<maxThreads
, 有空闲线程则新连接用空闲线程,无则建个线程; 连接数>=maxThreads,则新连接进入队列。
分析: accept了maxConnections个连接后,1. 如果有新连接过来,则进入监听套接字的backlog; 2.如果maxConnections>maxThreads,那么maxConnections-maxThreads个连接进入线程池的队列里。
分析: nginx反向代理到tomcat时,出现过这么个情况:nginx部分日志显示上游响应时间很长,但是tomcat access log显示处理时间都很短,这是为什么呢? 一个可能的原因是socket连接数满了(并且每个长连接服务100个http后关闭,才处理新连接),新连接进入backlog,nginx的时间表现为backlog呆的时间+线程池队列呆的时间+处理时间即accesslog里的时间,而tomcat access日志里的时间为请求接收直到响应发送完毕的时间。
conf/server.xml文件里,
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b %D" />
%D为毫秒,即为access log里的时间
2.2 工作线程(线程池)
线程池在在AbstractEndpoint中创建。
AbstractEndpoint<Socket>
|
JIoEndpoint
工作线程代码:JIoEndpoint$SocketProcessor.run() \
线程名:http-bio-端口号-exec-
关键逻辑
public void run() {
try {
处理socket(一次http请求)
} finally {
不关闭socket(长连接),则封装socket为runnable对象(JIoEndpoint$SocketProcessor对象)提交给线程池;
}
}
从单个线程来看:服务一个http请求后,将socket放到线程池,自己再去任务队列里取任务。 这种方式的好处是 连接数多于线程数时,所有连接都公平的轮询得到处理;缺点是socket任务不断的入队出队在频率极高下也会是个瓶劲。
2.3 命令监听线程
conf/server.xml文件里
<Server port="8005" shutdown="SHUTDOWN">
</Server>
接收shutdown 之类的命令