tomcat服务器解析(一)

7 篇文章 0 订阅

httpservlet自己并不能独立运行,需要依赖于一个web容器才能够运行。维基百科中对httpservlet和web容器的关系做了简要说明

 A web container (also known as a servlet container) is essentially the component of a web server that interacts with the servlets. The web container is responsible for managing the lifecycle of servlets, mapping a URL to a particular servlet and ensuring that the URL requester has the correct access rights. 


apache的官网(http://tomcat.apache.org/tomcat-8.0-doc/config/index.html)上有相应的文档对容器的配置和线程的关系进行说明,在介绍HTTP Connector时,有如下的描述。

 At server startup time, this Connector will create a number of request processing threads (based on the value configured for the  minSpareThreads attribute).  Each incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the  maxThreads attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the  Connector, up to the configured maximum (the value of the  acceptCount attribute). Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them. 

注意其中飘红的字段部分, 每个到达这个Connector的请求都有一个对应的线程去处理,所以已经可以确认,每个请求是会有一个对应的线程去进行处理。现在只需要确认,线程的创建时机,调用时机即可。


 The Executor represents a thread pool that can be shared between components in Tomcat.  Historically there has been a thread pool per connector created but this allows you to share a thread pool, between (primarly) connector but also other components when those get configured to support executors。

注意飘红的字,以前的版本是每个connector都有一个自己的线程池。这一点,在tomcat-5.5的http connector的文档中有相应的说明(http://tomcat.apache.org/tomcat-5.5-doc/config/http.html),原文为“At server startup time, this Connector will create a number of request processing threads”, 说明connector在启动的时候就会创建一个线程池,。但在tomcat-6.0的http connector的文档中,这句话被去掉了。同时相应的
https://tomcat.apache.org/tomcat-4.1-doc/catalina/docs/api/org/apache/catalina/connector/http/HttpConnector.html的文档中说明HttpConnector在后续的版本中被移除了。
一些新版本的tomcat代码在官网上可以找到,对于一些较早的版本,可以从这里(http://www.java2s.com/Code/Jar/c/Downloadcatalinasourcesjar.htm)找到(catalina-source.jar)相应的源代码 。通过catalian.jar的源代码上也能看出HttpConnector类的变化。

通过上面的对比可以猜测,线程池是在服务中配置的,在connector之间共享的。

OK,官网上的理论介绍到这里就差不多了,下面通过源代码来进行解读。官网上有提到Server的实现是org.apache.catalina.core.StandardServer 通过这个类找到相应的jar包,catalina.jar。借用一个已有的maven工程引入catalina.jar包的maven依赖,相关的maven依赖如下

< dependency >
      < groupId  > org.apache.tomcat  </ groupId >
      < artifactId  > tomcat-catalina </ artifactId  >
      < version  > 8.0.20  </ version >
</ dependency >


ps: jetty的代码参考  org.eclipse.jetty.server.Server;  org.eclipse.jetty.server.Connector; ---- 可作为对比学习的参考

在详细看代码之前,首先得了解官网中关于tomcat的web服务器的几个概念 server、service、connector以及engine

server就代表一个web容器,代表一个服务器,其它的几个概念都是属于server的一部分
service表示web容器能够提供的某项服务。如果把server按照功能粒度进行细化,那么每个单元就是一个service
connector和engine是组成server的两个组成部分。engine是执行用户的请求的引擎,connector是请求执行的通道。connector本身并不做请求的处理,只是提供通道功能。


StandardServer代表一个服务器实例,它有一个Service数组的属性,为当前这个Server所拥有的service。这里省去了其它与本次讨论无关的属性

public  final  class  StandardServer  extends  LifecycleMBeanBase  implements  Server{
     
     /**
     * The set of Services associated with this Server.
     */
     private  Service  services [] =  new  Service[0];
     private  final  Object  servicesLock  =  new  Object();

}

StandardService表示一项具体的服务能力,它有一个Connector数组的属性,是属于这个Service的连接器。
public  class  StandardService  extends  LifecycleMBeanBase  implements  Service {

     /**
     * The  <code> Server </code>  that owns this Service, if any.
     */
     private  Server server =  null ;
      /**
     * The set of Connectors associated with this Service.
     */
     protected  Connector connectors[] =  new  Connector[0];
     private  final  Object connectorsLock =  new  Object();

     protected  final  ArrayList<Executor> executors =  new  ArrayList<>();

      /**
     * The Container associated with this Service.
     */
     protected  Container container =  null ;
}
上面列出了与我们讨论相关的一些Service的属性。server和connectors不用再多说,container可作为一个Engine来作为服务的执行引擎。executors代表为这个服务配置的线程池,可在多个Connector之间共享。

从代码结构上,已经大致与上面的结论对应起来。那么,具体线程池的初始化、调用,请求的解析又是如何完成的呢?

因为Connector( org.apache.catalina.connector.Connector )是请求传递的通道,并且在不同版本之间有过较大结构升级,我们以Connector为突破口来学习。

来看下构造函数和启动函数

public  Connector(String protocol) {
        setProtocol(protocol);
         // Instantiate protocol handler
        ProtocolHandler p =  null ;
         try  {
            Class<?> clazz = Class.forName(protocolHandlerClassName);
            p = (ProtocolHandler) clazz.newInstance();
        }  catch  (Exception e) {
            log.error(sm.getString(
                     "coyoteConnector.protocolHandlerInstantiationFailed"  ), e);
        }  finally  {
             this .protocolHandler = p;
        }

         if  (!Globals.STRICT_SERVLET_COMPLIANCE) {
            URIEncoding =  "UTF-8" ;
            URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
        }
    }
@ Override
     protected  void  initInternal()  throws  LifecycleException {

         super .initInternal();

         // Initialize adapter
        adapter =  new  CoyoteAdapter( this );
        protocolHandler.setAdapter(adapter);

         // Make sure parseBodyMethodsSet has a default
         if (  null  == parseBodyMethodsSet ) {
            setParseBodyMethods(getParseBodyMethods());
        }

         if  (protocolHandler.isAprRequired() &&
                !AprLifecycleListener.isAprAvailable()) {
             throw  new  LifecycleException(
                    sm.getString(  "coyoteConnector.protocolHandlerNoApr"  ,
                            getProtocolHandlerClassName()));
        }

         try  {
            protocolHandler.init();
        }  catch  (Exception e) {
             throw  new  LifecycleException
                (sm.getString
                 (  "coyoteConnector.protocolHandlerInitializationFailed"  ), e);
        }
    }

     /**
     * Begin processing requests via this Connector.
     *
     *  @exception  LifecycleException if a fatal startup error occurs
     */
     @ Override
     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);
        }
    }

构造函数,主要是初始化一个ProtocolHandler对象。初始化函数,对这个PrococolHandler对象进行初始化。启动函数,则主要是启动这个对象。

ProtocolHandler相关的信息如下
    /**
     * Coyote Protocol handler class name.
     * Defaults to the Coyote HTTP/1.1 protocolHandler.
     */
     protected  String protocolHandlerClassName =  "org.apache.coyote.http11.Http11NioProtocol"  ;

到这里为止,还没有发现与处理http请求和调用线程池相关的地方。所以,继续去看Http11NioProtocol这个类。

Http11NioProtocol所处的类继承关系如下

Http11NioProtocol  extends  AbstractHttp11JsseProtocol<NioChannel>

public  abstract  class  AbstractHttp11JsseProtocol <S>   extends  AbstractHttp11Protocol<S> 

public  abstract  class  AbstractHttp11Protocol<S>  extends  AbstractProtocol<S>


在ProtocolHandler上的init和start所执行的操作,通过AbstractProtocol这两个方法的实现来看

public  void  init()  throws  Exception {
        
          // 这里省略了调试日志和rpc服务注册相关的代码。。。
        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));

         try  {
            endpoint.init();
        }  catch  (Exception ex) {
            getLog().error(sm.getString(  "abstractProtocolHandler.initError"  ,
                    getName()), ex);
             throw  ex;
        }
    }


@ Override
     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;
        }
    }


从代码上看AbstractProtocol本身并不做太多请求解析处理相关的事情,它是把事情委托给了一个AbstractEndpoint对象来完成。类似init和start生命周期,其它的几个生命周期如pause、resume、stop、destory也主要是通过AbstractEndpoint对象来完成。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值