Tomcat7中一次请求处理的前世今生(一)处理线程的产生
2014-09-11 23:15
399 查看
在默认的配置下Tomcat启动好之后会看到后台上总共有6个线程在运行。其中1个用户线程,剩下5个为守护线程(如下图所示)。
如果你对用户线程、守护线程等概念不熟悉,请参看前一篇文章——Tomcat7服务器关闭原理。
这里重点关注以http-bio-8080开头的两个守护线程(即http-bio-8080-Acceptor-0和http-bio-8080-AsyncTimeout),因为这是我们在Tomcat的默认配置下发布web应用时实际处理请求的线程。先看下这两个线程在容器启动时是如何产生和启动的。
在前面将Tomcat启动的系列文章中看到Tomcat容器启动时会用Digester读取server.xml文件产生相应的组件对象并采取链式调用的方式调用它们的init和start方法,在Digester读取到server.xml中的connector节点时是这么处理的:
Java代码
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
以上代码见org.apache.catalina.startup.Catalina类的366到372行。所以在碰到server.xml文件中的Server/Service/Connector节点时将会触发ConnectorCreateRule类的begin方法的调用:
Java代码
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Service svc = (Service)digester.peek();
Executor ex = null;
if ( attributes.getValue("executor")!=null ) {
ex = svc.getExecutor(attributes.getValue("executor"));
}
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
在第8行,会根据配置文件中Server/Service/Connector节点的protocol属性调用org.apache.catalina.connector.Connector类的构造方法,而默认情况下server.xml文件中Server/Service/Connector节点共有两处配置:
Xml代码
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
先看第一个Connector节点,调用Connector的构造方法时会传入字符串HTTP/1.1
Java代码
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
这里先会执行org.apache.catalina.connector.Connector类的setProtocol方法:
Java代码
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
所以此时会调用setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")从而将Connector类实例变量protocolHandlerClassName值设置为org.apache.coyote.http11.Http11Protocol,接下来在Connector的构造方法中就会根据protocolHandlerClassName变量的值产生一个org.apache.coyote.http11.Http11Protocol对象,并将该对象赋值给Connector类的实例变量protocolHandler。在Http11Protocol类的构造方法中会产生一个org.apache.tomcat.util.net.JIoEndpoint对象:
Java代码
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
几个相关对象的构造方法调用时序图如下所示,其中org.apache.coyote.AbstractProtocol是org.apache.coyote.http11.Http11Protocol的父类org.apache.tomcat.util.net.AbstractEndpoint是org.apache.tomcat.util.net.JIoEndpoint的父类。
接下来容器启动各组件时会调用org.apache.catalina.connector.Connector的start方法,如前面分析Tomcat启动时所述,此时会调用org.apache.catalina.connector.Connector类的startInternal方法:
Java代码
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
mapperListener.start();
}
在第12行,将会调用实例变量protocolHandler的start方法。在上面分析Connector类的构造函数时发现protocolHandler变量的值就是org.apache.coyote.http11.Http11Protocol对象,所以此时将会调用该类的start方法。在Http11Protocol类中没有定义start方法,这里将会调用其父类org.apache.coyote.AbstractProtocol中的start方法:
Java代码
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
这里会调用endpoint对象的start方法,而endpoint是org.apache.tomcat.util.net.JIoEndpoint类的实例(在上面讲Http11Protocol类的构造方法时所提到),这里最终会执行该类的startInternal方法:
Java代码
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
startAcceptorThreads();
// Start async timeout thread
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
正是在这里产生并启动本文开头提到的http-bio-8080-Acceptor-0和http-bio-8080-AsyncTimeout两个线程。第17到22行就是产生和启动http-bio-8080-AsyncTimeout线程,第15行,这里调用父类org.apache.tomcat.util.net.AbstractEndpoint的startAcceptorThreads方法:
Java代码
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
/**
* Hook to allow Endpoints to provide a specific Acceptor implementation.
*/
protected abstract Acceptor createAcceptor();
在这里将产生和启动http-bio-8080-Acceptor-0线程。注意在构造该线程时第6行将会调用第20行的抽象方法,该方法的具体实现是在JIoEndpoint类中:
Java代码
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
以上便是本文开头所述的两个后台线程产生和启动的流程,其相关类调用的时序图如下图所示:
同理,ajp-bio-8009-Acceptor-0和ajp-bio-8009-AsyncTimeout两个守护线程的产生和启动方式也是一致的,不再赘述。
如果你对用户线程、守护线程等概念不熟悉,请参看前一篇文章——Tomcat7服务器关闭原理。
这里重点关注以http-bio-8080开头的两个守护线程(即http-bio-8080-Acceptor-0和http-bio-8080-AsyncTimeout),因为这是我们在Tomcat的默认配置下发布web应用时实际处理请求的线程。先看下这两个线程在容器启动时是如何产生和启动的。
在前面将Tomcat启动的系列文章中看到Tomcat容器启动时会用Digester读取server.xml文件产生相应的组件对象并采取链式调用的方式调用它们的init和start方法,在Digester读取到server.xml中的connector节点时是这么处理的:
Java代码
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
以上代码见org.apache.catalina.startup.Catalina类的366到372行。所以在碰到server.xml文件中的Server/Service/Connector节点时将会触发ConnectorCreateRule类的begin方法的调用:
Java代码
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Service svc = (Service)digester.peek();
Executor ex = null;
if ( attributes.getValue("executor")!=null ) {
ex = svc.getExecutor(attributes.getValue("executor"));
}
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
在第8行,会根据配置文件中Server/Service/Connector节点的protocol属性调用org.apache.catalina.connector.Connector类的构造方法,而默认情况下server.xml文件中Server/Service/Connector节点共有两处配置:
Xml代码
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
先看第一个Connector节点,调用Connector的构造方法时会传入字符串HTTP/1.1
Java代码
public Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
}
这里先会执行org.apache.catalina.connector.Connector类的setProtocol方法:
Java代码
public void setProtocol(String protocol) {
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
所以此时会调用setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")从而将Connector类实例变量protocolHandlerClassName值设置为org.apache.coyote.http11.Http11Protocol,接下来在Connector的构造方法中就会根据protocolHandlerClassName变量的值产生一个org.apache.coyote.http11.Http11Protocol对象,并将该对象赋值给Connector类的实例变量protocolHandler。在Http11Protocol类的构造方法中会产生一个org.apache.tomcat.util.net.JIoEndpoint对象:
Java代码
public Http11Protocol() {
endpoint = new JIoEndpoint();
cHandler = new Http11ConnectionHandler(this);
((JIoEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
几个相关对象的构造方法调用时序图如下所示,其中org.apache.coyote.AbstractProtocol是org.apache.coyote.http11.Http11Protocol的父类org.apache.tomcat.util.net.AbstractEndpoint是org.apache.tomcat.util.net.JIoEndpoint的父类。
接下来容器启动各组件时会调用org.apache.catalina.connector.Connector的start方法,如前面分析Tomcat启动时所述,此时会调用org.apache.catalina.connector.Connector类的startInternal方法:
Java代码
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
mapperListener.start();
}
在第12行,将会调用实例变量protocolHandler的start方法。在上面分析Connector类的构造函数时发现protocolHandler变量的值就是org.apache.coyote.http11.Http11Protocol对象,所以此时将会调用该类的start方法。在Http11Protocol类中没有定义start方法,这里将会调用其父类org.apache.coyote.AbstractProtocol中的start方法:
Java代码
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
getName()));
try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
getName()), ex);
throw ex;
}
}
这里会调用endpoint对象的start方法,而endpoint是org.apache.tomcat.util.net.JIoEndpoint类的实例(在上面讲Http11Protocol类的构造方法时所提到),这里最终会执行该类的startInternal方法:
Java代码
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
if (getExecutor() == null) {
createExecutor();
}
initializeConnectionLatch();
startAcceptorThreads();
// Start async timeout thread
Thread timeoutThread = new Thread(new AsyncTimeout(),
getName() + "-AsyncTimeout");
timeoutThread.setPriority(threadPriority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
}
正是在这里产生并启动本文开头提到的http-bio-8080-Acceptor-0和http-bio-8080-AsyncTimeout两个线程。第17到22行就是产生和启动http-bio-8080-AsyncTimeout线程,第15行,这里调用父类org.apache.tomcat.util.net.AbstractEndpoint的startAcceptorThreads方法:
Java代码
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
/**
* Hook to allow Endpoints to provide a specific Acceptor implementation.
*/
protected abstract Acceptor createAcceptor();
在这里将产生和启动http-bio-8080-Acceptor-0线程。注意在构造该线程时第6行将会调用第20行的抽象方法,该方法的具体实现是在JIoEndpoint类中:
Java代码
@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
return new Acceptor();
}
以上便是本文开头所述的两个后台线程产生和启动的流程,其相关类调用的时序图如下图所示:
同理,ajp-bio-8009-Acceptor-0和ajp-bio-8009-AsyncTimeout两个守护线程的产生和启动方式也是一致的,不再赘述。
相关文章推荐
- Tomcat一次请求处理的前世今生(一) 处理线程的产生
- Tomcat一次请求处理的前世今生(二)Socket连接转换成内置请求对象
- Tomcat一次请求处理的前世今生 (三) 请求与容器具体组件的匹配
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- 用tomcat处理n个请求来说明,什么是进程,什么是线程
- 用tomcat处理n个请求来说明,什么是进程,什么是线程
- java的服务是每收到一个请求就新开一个线程来处理吗?tomcat呢?
- 一次web请求,进入tomcat和守护线程
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- Tomcat在处理GET和POST请求时产生的乱码问题
- [Tomcat6.0源码]请求的处理二Request