正文
在默认的配置下Tomcat启动好之后会看到后台上总共有6个线程在运行。其中1个用户线程,剩下5个为守护线程(如下图所示)。
如果你对用户线程、守护线程等概念不熟悉,请参看前一篇文章——
Tomcat 7 服务器关闭原理
。
这里重点关注以 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 节点时是这么处理的:
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 方法的调用:
1 public void begin(String namespace, String name, Attributes attributes)
2 throws Exception {
3 Service svc = (Service)digester.peek();
4 Executor ex = null;
5 if ( attributes.getValue("executor" )!=null ) {
6 ex = svc.getExecutor(attributes.getValue("executor" ));
7 }
8 Connector con = new Connector(attributes.getValue("protocol" ));
9 if ( ex != null ) _setExecutor(con,ex);
10
11 digester.push(con);
12 }
在第 8 行,会根据配置文件中 Server/Service/Connector 节点的 protocol 属性调用
org.apache.catalina.connector.Connector
类的构造方法,而默认情况下 server.xml 文件中 Server/Service/Connector 节点共有两处配置:
<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
1 public Connector(String protocol) {
2 set Protocol(protocol);
3 // Instantiate protocol handler
4 try {
5 Class<?> clazz = Class.forName(protocolHandlerClassName);
6 this.protocolHandler = (ProtocolHandler) clazz.newInstance();
7 } catch (Exception e) {
8 log.error(sm.getString(
9 "coyoteConnector.protocolHandlerInstantiationFailed" ), e);
10 }
11 }
这里先会执行
org.apache.catalina.connector.Connector
类的 setProtocol 方法:
1 public void set Protocol(String protocol) {
2
3 if (AprLifecycleListener.isAprAvailable()) {
4 if ("HTTP/1.1" .equals(protocol)) {
5 set ProtocolHandlerClassName
6 ("org.apache.coyote.http11.Http11AprProtocol" );
7 } else if ("AJP/1.3" .equals(protocol)) {
8 set ProtocolHandlerClassName
9 ("org.apache.coyote.ajp.AjpAprProtocol" );
10 } else if (protocol != null) {
11 set ProtocolHandlerClassName(protocol);
12 } else {
13 set ProtocolHandlerClassName
14 ("org.apache.coyote.http11.Http11AprProtocol" );
15 }
16 } else {
17 if ("HTTP/1.1" .equals(protocol)) {
18 set ProtocolHandlerClassName
19 ("org.apache.coyote.http11.Http11Protocol" );
20 } else if ("AJP/1.3" .equals(protocol)) {
21 set ProtocolHandlerClassName
22 ("org.apache.coyote.ajp.AjpProtocol" );
23 } else if (protocol != null) {
24 set ProtocolHandlerClassName(protocol);
25 }
26 }
27
28 }
所以此时会调用
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
对象: