Tomcat7中一次请求处理的前世今生(一)处理线程的产生
转载自http://tyrion.iteye.com/blog/1927658
在默认的配置下Tomcat启动好之后会看到后台上总共有6个线程在运行。其中1个用户线程,剩下5个为守护线程(如下图所示)。
这里重点关注以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节点时是这么处理的:
1 digester.addRule("Server/Service/Connector", 2 new ConnectorCreateRule()); 3 digester.addRule("Server/Service/Connector", 4 new SetAllPropertiesRule(new String[]{"executor"})); 5 digester.addSetNext("Server/Service/Connector", 6 "addConnector", 7 "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节点共有两处配置:
1 <Connector port="8080" protocol="HTTP/1.1" 2 connectionTimeout="20000" 3 redirectPort="8443" /> 4 5 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
先看第一个Connector节点,调用Connector的构造方法时会传入字符串HTTP/1.1
1 public Connector(String protocol) { 2 setProtocol(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 setProtocol(String protocol) { 2 3 if (AprLifecycleListener.isAprAvailable()) { 4 if ("HTTP/1.1".equals(protocol)) { 5 setProtocolHandlerClassName 6 ("org.apache.coyote.http11.Http11AprProtocol"); 7 } else if ("AJP/1.3".equals(protocol)) { 8 setProtocolHandlerClassName 9 ("org.apache.coyote.ajp.AjpAprProtocol"); 10 } else if (protocol != null) { 11 setProtocolHandlerClassName(protocol); 12 } else { 13 setProtocolHandlerClassName 14 ("org.apache.coyote.http11.Http11AprProtocol"); 15 } 16 } else { 17 if ("HTTP/1.1".equals(protocol)) { 18 setProtocolHandlerClassName 19 ("org.apache.coyote.http11.Http11Protocol"); 20 } else if ("AJP/1.3".equals(protocol)) { 21 setProtocolHandlerClassName 22 ("org.apache.coyote.ajp.AjpProtocol"); 23 } else if (protocol != null) { 24 setProtocolHandlerClassName(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对象:
1 public Http11Protocol() { 2 endpoint = new JIoEndpoint(); 3 cHandler = new Http11ConnectionHandler(this); 4 ((JIoEndpoint) endpoint).setHandler(cHandler); 5 setSoLinger(Constants.DEFAULT_CONNECTION_LINGER); 6 setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT); 7 setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY); 8 }
几个相关对象的构造方法调用时序图如下所示,其中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方法:
1 protected void startInternal() throws LifecycleException { 2 3 // Validate settings before starting 4 if (getPort() < 0) { 5 throw new LifecycleException(sm.getString( 6 "coyoteConnector.invalidPort", Integer.valueOf(getPort()))); 7 } 8 9 setState(LifecycleState.STARTING); 10 11 try { 12 protocolHandler.start(); 13 } catch (Exception e) { 14 String errPrefix = ""; 15 if(this.service != null) { 16 errPrefix += "service.getName(): \"" + this.service.getName() + "\"; "; 17 } 18 19 throw new LifecycleException 20 (errPrefix + " " + sm.getString 21 ("coyoteConnector.protocolHandlerStartFailed"), e); 22 } 23 24 mapperListener.start(); 25 }
在第12行,将会调用实例变量protocolHandler的start方法。在上面分析Connector类的构造函数时发现protocolHandler变量的值就是org.apache.coyote.http11.Http11Protocol对象,所以此时将会调用该类的start方法。在Http11Protocol类中没有定义start方法,这里将会调用其父类org.apache.coyote.AbstractProtocol中的start方法:
1 public void start() throws Exception { 2 if (getLog().isInfoEnabled()) 3 getLog().info(sm.getString("abstractProtocolHandler.start", 4 getName())); 5 try { 6 endpoint.start(); 7 } catch (Exception ex) { 8 getLog().error(sm.getString("abstractProtocolHandler.startError", 9 getName()), ex); 10 throw ex; 11 } 12 }
这里会调用endpoint对象的start方法,而endpoint是org.apache.tomcat.util.net.JIoEndpoint类的实例(在上面讲Http11Protocol类的构造方法时所提到),这里最终会执行该类的startInternal方法:
1 @Override 2 public void startInternal() throws Exception { 3 4 if (!running) { 5 running = true; 6 paused = false; 7 8 // Create worker collection 9 if (getExecutor() == null) { 10 createExecutor(); 11 } 12 13 initializeConnectionLatch(); 14 15 startAcceptorThreads(); 16 17 // Start async timeout thread 18 Thread timeoutThread = new Thread(new AsyncTimeout(), 19 getName() + "-AsyncTimeout"); 20 timeoutThread.setPriority(threadPriority); 21 timeoutThread.setDaemon(true); 22 timeoutThread.start(); 23 } 24 }
正是在这里产生并启动本文开头提到的http-bio-8080-Acceptor-0和http-bio-8080-AsyncTimeout两个线程。第17到22行就是产生和启动http-bio-8080-AsyncTimeout线程,第15行,这里调用父类org.apache.tomcat.util.net.AbstractEndpoint的startAcceptorThreads方法:
1 protected final void startAcceptorThreads() { 2 int count = getAcceptorThreadCount(); 3 acceptors = new Acceptor[count]; 4 5 for (int i = 0; i < count; i++) { 6 acceptors[i] = createAcceptor(); 7 String threadName = getName() + "-Acceptor-" + i; 8 acceptors[i].setThreadName(threadName); 9 Thread t = new Thread(acceptors[i], threadName); 10 t.setPriority(getAcceptorThreadPriority()); 11 t.setDaemon(getDaemon()); 12 t.start(); 13 } 14 } 15 16 17 /** 18 * Hook to allow Endpoints to provide a specific Acceptor implementation. 19 */ 20 protected abstract Acceptor createAcceptor();
在这里将产生和启动http-bio-8080-Acceptor-0线程。注意在构造该线程时第6行将会调用第20行的抽象方法,该方法的具体实现是在JIoEndpoint类中:
1 @Override 2 protected AbstractEndpoint.Acceptor createAcceptor() { 3 return new Acceptor(); 4 }
以上便是本文开头所述的两个后台线程产生和启动的流程,其相关类调用的时序图如下图所示:

同理,ajp-bio-8009-Acceptor-0和ajp-bio-8009-AsyncTimeout两个守护线程的产生和启动方式也是一致的,不再赘述。

浙公网安备 33010602011771号