Tomcat7中一次请求处理的前世今生(一)处理线程的产生

原创 2014年09月11日 23:15:37

在默认的配置下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代码  收藏代码
  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方法的调用:

Java代码  收藏代码
  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节点共有两处配置:

Xml代码  收藏代码
  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

Java代码  收藏代码
  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方法:

Java代码  收藏代码
  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对象:

Java代码  收藏代码
  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方法:

Java代码  收藏代码
  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方法:

Java代码  收藏代码
  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方法:

Java代码  收藏代码
  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方法:

Java代码  收藏代码
  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类中:

Java代码  收藏代码
  1. @Override  
  2. protected AbstractEndpoint.Acceptor createAcceptor() {  
  3.     return new Acceptor();  
  4. }  

以上便是本文开头所述的两个后台线程产生和启动的流程,其相关类调用的时序图如下图所示:


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

java《前世今生》

什么是java? 早期oak就是java的前世,          "今生提着刀,还你前世笑。来世未必能遇到。" oak前期不算是编程语言,是传统的解释性,中间经过更新换代,期间也改过名,直...
  • qq_30240677
  • qq_30240677
  • 2016年06月23日 20:06
  • 391

Tomcat一次请求处理的前世今生(一) 处理线程的产生

在默认的配置下Tomcat启动好之后会看到后台上总共有6个线程在运行。其中1个用户线程,剩下5个为守护线程(如下图所示)。 如果你对用户线程、守护线程等概念不熟悉,请参看前一篇文章——Tomca...
  • juroney1
  • juroney1
  • 2014年04月30日 02:38
  • 543

Linux 前世今生

Unix Unix的前身为Multics,贝尔实验室参与了这个操作系统的研发,但因为开发速度太慢,贝尔实验室决定放弃这个计划。贝尔实验室的工程师,汤普逊和里奇,在此时自行开发了Unix。第一版UNI...
  • cdl2008sky
  • cdl2008sky
  • 2014年12月24日 16:30
  • 4307

【机器学习】Logistic Regression 的前世今生(理论篇)

Logistic Regression可以说是机器学习的入门算法。不过,你真的有把握能够把LR从头到脚讲一遍吗?你会现场建模,数学推导?你知道它的正则化的作用?你能讲清楚它跟MaxEnt最大熵模型的关...
  • cyh24
  • cyh24
  • 2015年12月19日 17:36
  • 13153

史上最容易懂的云计算的前世今生

云计算的整个过程,用一个词来讲就是“分久必合,合久必分”。 云计算其实主要解决了四个方面的内容:计算,网络,存储,应用。前三者是资源层面的,最后是应用层面的。计算是CPU和内存,为啥?1+1这个最...
  • popsuper1982
  • popsuper1982
  • 2016年12月28日 19:40
  • 967

(1)hadoop学习——hadoop的前世今生

事物存在必然有它的意义,hadoop的发展可以说是必然的趋势。当数据进入爆发式增长的时代,摩尔定律变得越来越不适用,原有的存储方式和计算方式已经跟不上处理的节奏了。 原有的对应方式:加大磁盘空间(1T...
  • chenKFKevin
  • chenKFKevin
  • 2017年03月10日 09:51
  • 605

CoDeSys的前世今生

        工作以及网上看到不少人说,CoDeSys和西门子step7,在德国都属于标准过程,牛逼的小朋友都可以用其编程,不知真假,相信无风不起浪,多少有些依据,看看国內清一色的日系编程,我...
  • PINBODEXIAOZHU
  • PINBODEXIAOZHU
  • 2016年03月10日 09:03
  • 2482

深度学习的前世今生

好像最近人人都在谈人工智能,那人工智能到底是个啥。早期的人工智能项目致力于将世界的知识进行形式化的语言编码,计算机通过形式化的语言来理解既定的规则,这就是知识图谱的方法。然而该方法下,机器不具备自动学...
  • m0_37799949
  • m0_37799949
  • 2017年04月09日 15:33
  • 239

Android零基础入门第1节:Android的前世今生

Android已经诞生十多年了,但是你真的了解其前世今生吗?
  • cqkxzsxy
  • cqkxzsxy
  • 2017年07月10日 12:20
  • 1271

第二十三篇:深度美文:浅析人脸检测之Haar分类器方法 - ello - 博客园

转载自:浅析人脸检测之Haar分类器方法 - ello - 博客园 http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html 再...
  • liaojiacai
  • liaojiacai
  • 2015年10月23日 11:17
  • 573
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Tomcat7中一次请求处理的前世今生(一)处理线程的产生
举报原因:
原因补充:

(最多只允许输入30个字)