zoukankan      html  css  js  c++  java
  • 探秘Tomcat——连接篇

      前两篇我们分别粗线条和细粒度的讲解了tomcat的服务是如何启动以及连接器Connector和容器Container又分别是如何被启动的

      本篇我们主要侧重tomcat中server、service以及connector和container之间是如何相互关联起来的。在此之前,我们分别看下这个类中的一些主要方法,尤其是用于相互关联绑定的方法。

      Server:(Server代表了整个Catalina容器,一个server可以包含一个或多个Services)

     1   getInfo                    //获取server的版本
     2     getGlobalNamingResources
     3     setGlobalNamingResources
     4     getPort                    //返回监听关闭server的端口
     5     setPort
     6     getShutdown                //返回关闭server的命令字符串比如"SHUTDOWN"
     7     setShutdown
     8     addService                //在该server上添加一个service
     9     await                    //一直监听,直到出现shutdown指令
    10     findService                //返回指定名称的service
    11     findServices            //返回所有在这个server上的services集合
    12     removeService
    13     initialize

      Service:(Service是一组包含了一个Container和一个或多个Connector的集合)

     1   getContainer            //返回容器,该容器用于处理Service上的Connectors发送过来请求
     2     setContainer
     3     getInfo                    //返回Service的版本信息
     4     getName                    //返回该Service的名字
     5     setName
     6     getServer                //返回与此Service关联的Server,这与Server中的addService遥相呼应
     7     setServer                //绑定一个Server
     8     addConnector            //添加一个Connector
     9     findConnectors            //返回该Service上的所有Connector    
    10     removeConnector            //删除指定的Connector,同时也以为该Connector与Container也解除联系
    11     initialize
    12     addExecutor                //添加一个执行器
    13     findExecutors
    14     getExecutor
    15     removeExecutor

      Connector:(前面已经说过,一个Service中可以包含多个Container,但是只会有一个Connector,而Container有多层实现关系,并且有自己的实现规范,所以定义成了接口,而这里的Connector就是一个类而非接口)

     1 Connector
     2     Connector                    //构造函数,其中有设置Connector要用到的协议
     3     getProperty                    //根据属性名,返回属性值
     4     setProperty
     5     getAttribute                //也是根据属性名,返回属性值,但是getProperty返回的是String类型,这里是Object对象
     6     setAttribute
     7     removeProperty
     8     getService                    //返回与之绑定的Service
     9     setService                    //绑定Service
    10     getAllowTrace
    11     setAllowTrace                //设置allowTrace,用于跟踪http的信息
    12     isAvailable                    //判断是否可用于处理request,里面判断的标记是started,这意味着只有Connector启动了才能用于处理request
    13     getBufferSize                
    14     setBufferSize
    15     getContainer                //返回当前Connector移交request的接收Container对象
    16     setContainer
    17     getEmptySessionPath
    18     setEmptySessionPath
    19     getEnableLookups
    20     setEnableLookups
    21     getInfo                        //返回Connector的版本信息
    22     getMapper
    23     getMaxHeaderCount            //返回Container允许的最大headers个数
    24     setMaxHeaderCount
    25     getMaxParameterCount        //返回GET和POST方法的最大个数
    26     setMaxParameterCount
    27     ...
    28     getPort                        //返回监听request的端口
    29     setPort
    30     getProtocol                    //返回使用到的protocol handler,有Http/1.1和AJP/1.3
    31     setProtocol
    32     getProtocolHandlerClassName
    33     setProtocolHandlerClassName
    34     getProtocolHandler
    35     getProxyName                //设置代理的名字
    36     setProxyName
    37     getProxyPort
    38     setProxyPort
    39     getRedirectPort                //重定向端口,如果
    40     setRedirectPort
    41     getScheme
    42     setScheme
    43     getSecure
    44     setSecure
    45     getURIEncoding                //返回URI编码
    46     setURIEncoding
    47     getUseBodyEncodingForURI
    48     setUseBodyEncodingForURI
    49     getXpoweredBy
    50     setXpoweredBy
    51     setUseIPVHosts
    52     getUseIPVHosts
    53     getExecutorName
    54     createRequest                //创建或指派并返回Request对象,这里的Request有和Container关联
    55     createResponse
    56     addLifecycleListener
    57     findLifecycleListeners
    58     removeLifecycleListener
    59     createObjectName
    60     initialize                    //初始化Connector对象
    61     pause                        
    62     resume
    63     start
    64     stop
    65     ...
    66     init
    67     destroy
    68     toString

      Container:(Container可以执行来自客户端的Request请求,并返回相应的Response)

    getInfo
        getLoader                    //返回与此Container相关的Loader对象,如果没有Loader,则返回与其父Container关联的Loader
        setLoader
        getLogger                    //返回Logger对象,用于打印log,同理如果当前没有Logger对象,则寻找父级Logger
        getManager                    //返回Manager对象
        setManager
        getMappingObject            //返回JMX对象名字
        getObjectName
        getPipeline                    //返回与此Container相关的用于管理Valves的Pipeline
        getCluster
        setCluster
        getBackgroundProcessorDelay
        setBackgroundProcessorDelay
        getName
        setName
        getParent                    //返回父级Container
        setParent
        getParentClassLoader        //返回父级类加载器    
        setParentClassLoader
        getRealm
        setRealm
        getResources
        setResources
        backgroundProcess
        addChild                    //添加一个子容器,在添加之前,需要在子容器中先调用setParent方法
        addContainerListener        //添加事件监听器
        addPropertyChangeListener    //添加属性值变化监听器    
        findChild
        findChildren
        findContainerListeners
        invoke                        //执行具体的Request,并得到具体的Response对象
        removeChild
        removeContainerListener
        removePropertyChangeListener
        logAccess

    1.连接原理举例

      首先我们在Catalina类的load方法中调用了方法createStartDigester,该方法在之前几篇有介绍过,主要是对于加载的server.xml文件中定义各个组件之间的关系。

      比如方法中的片段:

    digester.addObjectCreate("Server",
                             "org.apache.catalina.core.StandardServer",
                             "className");
    digester.addSetProperties("Server");
    digester.addSetNext("Server",
                        "setServer",
                        "org.apache.catalina.Server");
    • addObjectCreate就是添加一个模式,当解析server.xml遇到Server的时候,就根据Server的className实例化一个Server对象,而默认实例化的类就是org.apache.catalina.core.StandardServer;
    • addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
    • addSetNext用于调用Server类的setServer方法,把当前Server添加进去。

      再比如这几行代码:

    digester.addObjectCreate("Server/Listener",
                             null, // MUST be specified in the element
                             "className");
    digester.addSetProperties("Server/Listener");
    digester.addSetNext("Server/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

      对应在server.xml中就是这几行

    1 <!--APR library loader. Documentation at /docs/apr.html -->
    2   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
    3   <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
    4   <Listener className="org.apache.catalina.core.JasperListener" />
    5   <!-- Prevent memory leaks due to use of particular java/javax APIs-->
    6   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    7   <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
    8   <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
    9   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    • 同理addObjectCreate说的是在Server下的Listeners,根据className创建listener对象;
    • addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
    • addSetNext用于调用Server类的addLifecycleLisntener方法,把server.xml中定义的5个监听器都实例化并添加到server上。

     

      还有关于Server和Service之间关系的代码

    digester.addObjectCreate("Server/Service",
                             "org.apache.catalina.core.StandardService",
                             "className");
    digester.addSetProperties("Server/Service");
    digester.addSetNext("Server/Service",
                        "addService",
                        "org.apache.catalina.Service");

      通过这些代码我们很容易理解Server和Service之间的关联关系(后面会详细介绍)

      除了Server和Service之间的从属关系,我们还可以看到Service和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");

      同理这里也是在Service下调用addConnector添加Connector(后面会详细介绍)

    2.Connector和Container以及Connector和Service何时连接?

      我们从Catalina的load方法开始,当执行到load中的digester.parse(inputSource)时,即跳转到Digester类的parse方法中,之后开始解析server.xml中依次遇到的各个元素。

      当遇到server元素的时候,在代码中方法的执行顺序为Catalina.load->Digester.parse->Digester.startElement

      startElement方法如下:

     1 public void startElement(String namespaceURI, String localName,
     2                          String qName, Attributes list)
     3         throws SAXException {
     4     boolean debug = log.isDebugEnabled();
     5 
     6     if (saxLog.isDebugEnabled()) {
     7         saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
     8                 qName + ")");
     9     }
    10 
    11     // Parse system properties
    12     list = updateAttributes(list);
    13 
    14     // Save the body text accumulated for our surrounding element
    15     bodyTexts.push(bodyText);
    16     if (debug) {
    17         log.debug("  Pushing body text '" + bodyText.toString() + "'");
    18     }
    19     bodyText = new StringBuffer();
    20 
    21     // the actual element name is either in localName or qName, depending
    22     // on whether the parser is namespace aware
    23     String name = localName;
    24     if ((name == null) || (name.length() < 1)) {
    25         name = qName;
    26     }
    27 
    28     // Compute the current matching rule
    29     StringBuffer sb = new StringBuffer(match);
    30     if (match.length() > 0) {
    31         sb.append('/');
    32     }
    33     sb.append(name);
    34     match = sb.toString();
    35     if (debug) {
    36         log.debug("  New match='" + match + "'");
    37     }
    38 
    39     // Fire "begin" events for all relevant rules
    40     List rules = getRules().match(namespaceURI, match);
    41     matches.push(rules);
    42     if ((rules != null) && (rules.size() > 0)) {
    43         for (int i = 0; i < rules.size(); i++) {
    44             try {
    45                 Rule rule = (Rule) rules.get(i);
    46                 if (debug) {
    47                     log.debug("  Fire begin() for " + rule);
    48                 }
    49                 rule.begin(namespaceURI, name, list);
    50             } catch (Exception e) {
    51                 log.error("Begin event threw exception", e);
    52                 throw createSAXException(e);
    53             } catch (Error e) {
    54                 log.error("Begin event threw error", e);
    55                 throw e;
    56             }
    57         }
    58     } else {
    59         if (debug) {
    60             log.debug("  No rules found matching '" + match + "'.");
    61         }
    62     }
    63 
    64 }
    Digester.startElement

    当执行到rule.begin(namespaceURI, name, list)这行的时候,通过调试信息可以看到该rule的className为org.apache.catalina.core.StandardServer,所以最终会进入StandardServer的构造函数中。

      另外,当解析server.xml到5个listener的时候,就会调用StandardServer的addLifecycleListener分别将这5个监听器实例化并添加到server上。

      继续解析,当解析到

    igester.addSetNext("Server/Service/Connector",
                        "addConnector",
                        "org.apache.catalina.connector.Connector");

      的时候就会跳转到StandardService的addConnector方法中

     1 public void addConnector(Connector connector) {
     2 
     3     synchronized (connectors) {
     4         connector.setContainer(this.container);
     5         connector.setService(this);
     6         Connector results[] = new Connector[connectors.length + 1];
     7         System.arraycopy(connectors, 0, results, 0, connectors.length);
     8         results[connectors.length] = connector;
     9         connectors = results;
    10 
    11         if (initialized) {
    12             try {
    13                 connector.initialize();
    14             } catch (LifecycleException e) {
    15                 log.error(sm.getString(
    16                         "standardService.connector.initFailed",
    17                         connector), e);
    18             }
    19         }
    20 
    21         if (started && (connector instanceof Lifecycle)) {
    22             try {
    23                 ((Lifecycle) connector).start();
    24             } catch (LifecycleException e) {
    25                 log.error(sm.getString(
    26                         "standardService.connector.startFailed",
    27                         connector), e);
    28             }
    29         }
    30 
    31         // Report this property change to interested listeners
    32         support.firePropertyChange("connector", null, connector);
    33     }
    34 
    35 }

      首先解析到的是server.xml中的这个connector

    <Connector port="8080" protocol="HTTP/1.1" 
                   connectionTimeout="20000" 
                   redirectPort="8443" />

      下面的两行代码交代了一个Connector是如何关联上Container和Service的:

    • connector.setContainer(this.container):说明了connector和container是如何关联的,调用connector对象的setContainer方法,而传进的值为this.Container,也就是当前StandardService的Container对象,这样就完成了Connector和Container之间的连接
    • connector.setService(this):对外这里绑定了当前的StandardService作为其从属的service。

     

    3.Service和Container是何时连接的?

      继续解析直到Catalina.createStartDigester定义的

    digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

      这时候会调用StandardService的setContainer方法:

     1 public void setContainer(Container container) {
     2 
     3     Container oldContainer = this.container;
     4     if ((oldContainer != null) && (oldContainer instanceof Engine))
     5         ((Engine) oldContainer).setService(null);
     6     this.container = container;
     7     if ((this.container != null) && (this.container instanceof Engine))
     8         ((Engine) this.container).setService(this);
     9     if (started && (this.container != null) &&
    10         (this.container instanceof Lifecycle)) {
    11         try {
    12             ((Lifecycle) this.container).start();
    13         } catch (LifecycleException e) {
    14             ;
    15         }
    16     }
    17     synchronized (connectors) {
    18         for (int i = 0; i < connectors.length; i++)
    19             connectors[i].setContainer(this.container);
    20     }
    21     if (started && (oldContainer != null) &&
    22         (oldContainer instanceof Lifecycle)) {
    23         try {
    24             ((Lifecycle) oldContainer).stop();
    25         } catch (LifecycleException e) {
    26             ;
    27         }
    28     }
    29 
    30     // Report this property change to interested listeners
    31     support.firePropertyChange("container", oldContainer, this.container);
    32 
    33 }

      当执行到((Engine) this.container).setService(this);这里会跳转到StandardEngine的setService交代了container是如何绑定StandardService的。

      并且在代码

    1 synchronized (connectors) {
    2     for (int i = 0; i < connectors.length; i++)
    3         connectors[i].setContainer(this.container);
    4 }

      我们可以看到通过遍历所有的connector,将其与container绑定。

    4.Server和Service又是何时连接的?

      继续解析直到Catalina.createStartDigerster中的

    digester.addSetNext("Server/Service",
                        "addService",
                        "org.apache.catalina.Service");

      会调用StandardServer的addService方法

     1 public void addService(Service service) {
     2 
     3     service.setServer(this);
     4 
     5     synchronized (services) {
     6         Service results[] = new Service[services.length + 1];
     7         System.arraycopy(services, 0, results, 0, services.length);
     8         results[services.length] = service;
     9         services = results;
    10 
    11         if (initialized) {
    12             try {
    13                 service.initialize();
    14             } catch (LifecycleException e) {
    15                 log.error(e);
    16             }
    17         }
    18 
    19         if (started && (service instanceof Lifecycle)) {
    20             try {
    21                 ((Lifecycle) service).start();
    22             } catch (LifecycleException e) {
    23                 ;
    24             }
    25         }
    26 
    27         // Report this property change to interested listeners
    28         support.firePropertyChange("service", null, service);
    29     }
    30 
    31 }

      service.setServer(this):该行会调用StandardService中的setServer为service绑定当前的StandardServer对象

    5.小结

      当server.xml中的rule解析完毕后,我们起码明白了:

    • Server和Service是如何关联的;
    • Service和Connector是如何关联的;
    • Service和Container是如何关联的;
    • Connector和Container是如何关联的

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。




    友情赞助

    如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

        1. 支付宝                          2. 微信

                          

  • 相关阅读:
    X Wing 数独Sudoku
    Vue.js—Difference between v-model and v-bind
    How to add dynamically attribute in VueJs
    how many types in javascript
    Why does JavaScript variable declaration at console results in “undefined” being printed?
    What’s the difference between “{}” and “[]” while declaring a JavaScript array?
    What’s the difference between “Array()” and “[]” while declaring a JavaScript array?
    vue-cli 和webpack
    OpenWrt For Support SkyEdge Gilat Modem Configure
    Webots 简介
  • 原文地址:https://www.cnblogs.com/bigdataZJ/p/TomcatSourceZJ6.html
Copyright © 2011-2022 走看看