佳句有约 發表於 2026-4-28 09:00:00

Tomcat Service的设计和实现:StandardService

<h2 id="理解思路">理解思路</h2>
<ul>
<li><strong>第一:类比StandardServer, 抓住StandardService整体类依赖结构来理解</strong></li>
</ul>
<p><img src="https://seven97-blog.oss-cn-hangzhou.aliyuncs.com/imgs/202603082029193.jpeg" alt="" loading="lazy"></p>
<ul>
<li><strong>第二:结合server.xml中service配置来理解</strong></li>
</ul>
<p>见下文具体阐述。</p>
<ul>
<li><strong>第三:结合Service Config官方配置文档</strong></li>
</ul>
<p>http://tomcat.apache.org/tomcat-9.0-doc/config/service.html</p>
<h2 id="service结构设计">Service结构设计</h2>
<blockquote>
<p>我们需要从高一点的维度去理解service的结构设计,而不是多少方法多少代码;这里的理解一定是要结合Server.xml中service配置部分对应理解。</p>
</blockquote>
<h3 id="serverxml">server.xml</h3>
<ul>
<li>首先要看下server.xml中Service的配置,这样你便知道了需要了解的4个部分</li>
</ul>
<pre><code class="language-xml">&lt;!--
    每个Service元素只能有一个Engine元素.元素处理在同一个&lt;Service&gt;中所有&lt;Connector&gt;元素接收到的客户请求
--&gt;

&lt;Service name="Catalina"&gt;
&lt;!-- 1. 属性说明
        name:Service的名称
--&gt;

    &lt;!--2. 一个或多个excecutors --&gt;
    &lt;!--
    &lt;Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
      maxThreads="150" minSpareThreads="4"/&gt;
    --&gt;

    &lt;!--
                3.Connector元素:
                        由Connector接口定义.&lt;Connector&gt;元素代表与客户程序实际交互的组件,它负责接收客户请求,以及向客户返回响应结果.
    --&gt;
    &lt;Connector port="80" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" /&gt;
    &lt;!-- 属性说明
                port:服务器连接器的端口号,该连接器将在指定端口侦听来自客户端的请求。
                enableLookups:如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名;
                                        若为false则不进行DNS查询,而是返回其ip地址。
                redirectPort:服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号。
                acceptCount:当所有可以使用的处理请求的线程都被用光时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,而返回Connection refused错误。
                connectionTimeout:等待超时的时间数(以毫秒为单位)。
                maxThreads:设定在监听端口的线程的最大数目,这个值也决定了服务器可以同时响应客户请求的最大数目.默认值为200。
                protocol:必须设定为AJP/1.3协议。
                address:如果服务器有两个以上IP地址,该属性可以设定端口监听的IP地址,默认情况下,端口会监听服务器上所有IP地址。
                minProcessors:服务器启动时创建的处理请求的线程数,每个请求由一个线程负责。
                maxProcessors:最多可以创建的处理请求的线程数。
                minSpareThreads:最小备用线程 。
                maxSpareThreads:最大备用线程。
                debug:日志等级。
                disableUploadTimeout:禁用上传超时,主要用于大数据上传时。
    --&gt;


    &lt;Connector port="8009" enableLookups="false" redirectPort="8443" protocol="AJP/1.3" /&gt;
    &lt;!-- 负责和其他HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时就需要用到这个连接器。 --&gt;
       
    &lt;!--
                4. Engine
    --&gt;
    &lt;Engine name="Catalina" defaultHost="localhost"&gt;
   
    &lt;/Engine&gt;
&lt;/Service&gt;
</code></pre>
<h3 id="service中的接口设计">Service中的接口设计</h3>
<ul>
<li><strong>公共属性</strong>, name等</li>
</ul>
<pre><code class="language-java">/**
* @return the name of this Service.
*/
public String getName();

/**
* Set the name of this Service.
*
* @param name The new service name
*/
public void setName(String name);
</code></pre>
<ul>
<li>父Server相关</li>
</ul>
<pre><code class="language-java">/**
* @return the &lt;code&gt;Server&lt;/code&gt; with which we are associated (if any).
*/
public Server getServer();

/**
* Set the &lt;code&gt;Server&lt;/code&gt; with which we are associated (if any).
*
* @param server The server that owns this Service
*/
public void setServer(Server server);

/**
* @return the parent class loader for this component. If not set, return
* {@link #getServer()} {@link Server#getParentClassLoader()}. If no server
* has been set, return the system class loader.
*/
public ClassLoader getParentClassLoader();

/**
* Set the parent class loader for this service.
*
* @param parent The new parent class loader
*/
public void setParentClassLoader(ClassLoader parent);

/**
* @return the domain under which this container will be / has been
* registered.
*/
public String getDomain();
</code></pre>
<ul>
<li>Connector相关</li>
</ul>
<pre><code class="language-java">/**
* Add a new Connector to the set of defined Connectors, and associate it
* with this Service's Container.
*
* @param connector The Connector to be added
*/
public void addConnector(Connector connector);

/**
* Find and return the set of Connectors associated with this Service.
*
* @return the set of associated Connectors
*/
public Connector[] findConnectors();

/**
* Remove the specified Connector from the set associated from this
* Service.The removed Connector will also be disassociated from our
* Container.
*
* @param connector The Connector to be removed
*/
public void removeConnector(Connector connector);
</code></pre>
<ul>
<li>Engine</li>
</ul>
<pre><code class="language-java">/**
* @return the &lt;code&gt;Engine&lt;/code&gt; that handles requests for all
* &lt;code&gt;Connectors&lt;/code&gt; associated with this Service.
*/
public Engine getContainer();

/**
* Set the &lt;code&gt;Engine&lt;/code&gt; that handles requests for all
* &lt;code&gt;Connectors&lt;/code&gt; associated with this Service.
*
* @param engine The new Engine
*/
public void setContainer(Engine engine);
</code></pre>
<ul>
<li>Excutor相关</li>
</ul>
<pre><code class="language-java">/**
* Adds a named executor to the service
* @param ex Executor
*/
public void addExecutor(Executor ex);

/**
* Retrieves all executors
* @return Executor[]
*/
public Executor[] findExecutors();

/**
* Retrieves executor by name, null if not found
* @param name String
* @return Executor
*/
public Executor getExecutor(String name);

/**
* Removes an executor from the service
* @param ex Executor
*/
public void removeExecutor(Executor ex);
</code></pre>
<h2 id="standardservice的实现">StandardService的实现</h2>
<p>属性和父Server相关比较简单,这里主要看下其它的方法:</p>
<h3 id="engine相关">Engine相关</h3>
<pre><code class="language-java">private Engine engine = null;

@Override
public Engine getContainer() {
    return engine;
}

@Override
public void setContainer(Engine engine) {
    Engine oldEngine = this.engine;
    if (oldEngine != null) {
      oldEngine.setService(null);
    }
    this.engine = engine;
    if (this.engine != null) {
      this.engine.setService(this);
    }
    if (getState().isAvailable()) {
      if (this.engine != null) {
            try {
                this.engine.start(); // 启动Engine
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.engine.startFailed"), e);
            }
      }
      // 重启Mapper - Restart MapperListener to pick up new engine.
      try {
            mapperListener.stop();
      } catch (LifecycleException e) {
            log.error(sm.getString("standardService.mapperListener.stopFailed"), e);
      }
      try {
            mapperListener.start();
      } catch (LifecycleException e) {
            log.error(sm.getString("standardService.mapperListener.startFailed"), e);
      }
      if (oldEngine != null) {
            try {
                oldEngine.stop();
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.engine.stopFailed"), e);
            }
      }
    }

    // 触发container属性变更事件
    support.firePropertyChange("container", oldEngine, this.engine);
}
</code></pre>
<h3 id="connectors相关">Connectors相关</h3>
<pre><code class="language-java">/**
* The set of Connectors associated with this Service.
*/
protected Connector connectors[] = new Connector;
private final Object connectorsLock = new Object();

/**
* Add a new Connector to the set of defined Connectors, and associate it
* with this Service's Container.
*
* @param connector The Connector to be added
*/
@Override
public void addConnector(Connector connector) {

    synchronized (connectorsLock) {
      connector.setService(this);
      Connector results[] = new Connector;
      System.arraycopy(connectors, 0, results, 0, connectors.length);
      results = connector;
      connectors = results;
    }

    try {
      if (getState().isAvailable()) {
            connector.start();
      }
    } catch (LifecycleException e) {
      throw new IllegalArgumentException(
                sm.getString("standardService.connector.startFailed", connector), e);
    }

    // Report this property change to interested listeners
    support.firePropertyChange("connector", null, connector);
}


public ObjectName[] getConnectorNames() {
    ObjectName results[] = new ObjectName;
    for (int i=0; i&lt;results.length; i++) {
      results = connectors.getObjectName();
    }
    return results;
}

/**
* 当前Service相关的所有Connectors.
*/
@Override
public Connector[] findConnectors() {
    return connectors;
}

/**
* 删除connector
*
* @param connector The Connector to be removed
*/
@Override
public void removeConnector(Connector connector) {

    synchronized (connectorsLock) {
      // 找到conector位置
      int j = -1;
      for (int i = 0; i &lt; connectors.length; i++) {
            if (connector == connectors) {
                j = i;
                break;
            }
      }
      if (j &lt; 0)
            return;
      if (connectors.getState().isAvailable()) {
            try {
                connectors.stop(); // 停止
            } catch (LifecycleException e) {
                log.error(sm.getString(
                        "standardService.connector.stopFailed",
                        connectors), e);
            }
      }
      connector.setService(null); // 去除父service绑定
      int k = 0;
      Connector results[] = new Connector;
      for (int i = 0; i &lt; connectors.length; i++) {
            if (i != j)
                results = connectors; // 后续connector向前移位
      }
      connectors = results;

      // 触发connector属性变更事件
      support.firePropertyChange("connector", connector, null);
    }
}
</code></pre>
<h3 id="executor相关">Executor相关</h3>
<p>CRUD方法,代码比较简单</p>
<pre><code class="language-java">/**
* Adds a named executor to the service
* @param ex Executor
*/
@Override
public void addExecutor(Executor ex) {
    synchronized (executors) {
      if (!executors.contains(ex)) {
            executors.add(ex);
            if (getState().isAvailable()) {
                try {
                  ex.start(); // 启动
                } catch (LifecycleException x) {
                  log.error(sm.getString("standardService.executor.start"), x);
                }
            }
      }
    }
}

/**
* Retrieves all executors
* @return Executor[]
*/
@Override
public Executor[] findExecutors() {
    synchronized (executors) {
      Executor[] arr = new Executor;
      executors.toArray(arr);
      return arr;
    }
}


/**
* Retrieves executor by name, null if not found
* @param executorName String
* @return Executor
*/
@Override
public Executor getExecutor(String executorName) {
    synchronized (executors) {
      for (Executor executor: executors) {
            if (executorName.equals(executor.getName()))
                return executor;
      }
    }
    return null;
}

/**
* Removes an executor from the service
* @param ex Executor
*/
@Override
public void removeExecutor(Executor ex) {
    synchronized (executors) {
      if ( executors.remove(ex) &amp;&amp; getState().isAvailable() ) {
            try {
                ex.stop(); // 停止
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.executor.stop"), e);
            }
      }
    }
}
</code></pre>
<h3 id="lifecycle相关模板方法">Lifecycle相关模板方法</h3>
<p>首先看 <strong>initInternal</strong> 方法</p>
<pre><code class="language-java">/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    if (engine != null) {
      engine.init();
    }

    // Initialize any Executors
    for (Executor executor : findExecutors()) {
      if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
      }
      executor.init();
    }

    // Initialize mapper listener
    mapperListener.init();

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
      for (Connector connector : connectors) {
            connector.init();
      }
    }
}
</code></pre>
<p>initInternal 代码很短,思路也很清晰,就是依次调用了这个成员变量的 init 方法</p>
<pre><code class="language-java">engine.init()
executor.init
mapperListener.init()
connector.init()
</code></pre>
<p><strong>startInternal 方法</strong></p>
<pre><code class="language-java">/**
* Start nested components ({@link Executor}s, {@link Connector}s and
* {@link Container}s) and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
*that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {

    if(log.isInfoEnabled())
      log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);

    // Start our defined Container first
    if (engine != null) {
      synchronized (engine) {
            engine.start();
      }
    }

    synchronized (executors) {
      for (Executor executor: executors) {
            executor.start();
      }
    }

    mapperListener.start();

    // Start our defined Connectors second
    synchronized (connectorsLock) {
      for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
      }
    }
}
</code></pre>
<p>startInternal 跟 initInternal 方法一样,也是依次调用</p>
<pre><code class="language-java">engine.start();
executor.start();
mapperListener.start();
connector.start();
</code></pre>
<h3 id="补充下mapperlistener">补充下MapperListener</h3>
<p>mapperListener 的作用是在 start 的时候将容器类对象注册到 Mapper 对象中。</p>
<pre><code class="language-java">/**
* Create mapper listener.
*
* @param service The service this listener is associated with
*/
public MapperListener(Service service) {
    this.service = service;
    this.mapper = service.getMapper();
}
service.getMapper() 返回的是 StandardService 对象的 mapper 成员变量。

/**
* Mapper.
*/
protected final Mapper mapper = new Mapper();
</code></pre>
<p>Mapper是 Tomcat 处理 Http 请求时非常重要的组件。Tomcat 使用 Mapper 来处理一个 Request 到 Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。</p>
<p>MapperListener 也是继承自 LifecycleMBeanBase,不过没有重载 initInternal 方法。</p>
<ul>
<li>startInternal 方法</li>
</ul>
<pre><code class="language-java">@Override
public void startInternal() throws LifecycleException {

    setState(LifecycleState.STARTING);

    Engine engine = service.getContainer();
    if (engine == null) {
      return;
    }

    findDefaultHost();

    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
      Host host = (Host) conHost;
      if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
            registerHost(host);
      }
    }
}
</code></pre>
<ul>
<li>findDefaultHost() 方法</li>
</ul>
<p>首先看 findDefaultHost() 方法</p>
<pre><code class="language-java">private void findDefaultHost() {

    Engine engine = service.getContainer();
    String defaultHost = engine.getDefaultHost();

    boolean found = false;

    if (defaultHost != null &amp;&amp; defaultHost.length() &gt; 0) {
      Container[] containers = engine.findChildren();

      for (Container container : containers) {
            Host host = (Host) container;
            if (defaultHost.equalsIgnoreCase(host.getName())) {
                found = true;
                break;
            }

            String[] aliases = host.findAliases();
            for (String alias : aliases) {
                if (defaultHost.equalsIgnoreCase(alias)) {
                  found = true;
                  break;
                }
            }
      }
    }

    if (found) {
      mapper.setDefaultHostName(defaultHost);
    } else {
      log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
    }
}
</code></pre>
<p>findDefaultHost() 是主要是找出 defaultHost ,并调用 <code>mapper.setDefaultHostName(defaultHost);</code> 这个 defaultHost 是 server.xml 的 <code>&lt;Engine&gt;</code> 标签的属性,一般都是 "localHost"。</p>
<p>从上面代码 for 代码块里可以看出,Host 是 Engine 的子 Container。for 语句就是找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。</p>
<ul>
<li>addListeners(engine) 方法</li>
</ul>
<pre><code class="language-java">/**
* Add this mapper to the container and all child containers
*
* @param container
*/
private void addListeners(Container container) {
    container.addContainerListener(this);
    container.addLifecycleListener(this);
    for (Container child : container.findChildren()) {
      addListeners(child);
    }
}
</code></pre>
<p>这个方法的作用是,将 MapperListener 这个监听器添加到 Engine 及其子容器中</p>
<ul>
<li>registerHost 调用 registerHost方法来注册 Engine 的字容器 Host。</li>
</ul>
<pre><code class="language-java">/**
* Register host.
*/
private void registerHost(Host host) {

    String[] aliases = host.findAliases();
    mapper.addHost(host.getName(), aliases, host);

    for (Container container : host.findChildren()) {
      if (container.getState().isAvailable()) {
            registerContext((Context) container);
      }
    }

    // Default host may have changed
    findDefaultHost();

    if(log.isDebugEnabled()) {
      log.debug(sm.getString("mapperListener.registerHost",
                host.getName(), domain, service));
    }
}
</code></pre>
<p>registerHost 方法先调用 mapper.addHost,然后调用 registerContext 方法注册 Host 的子容器 Context。 mapper.addHost 方法是将 Host 加入的 Mapper 类的的成员变量MappedHost[] hosts 中。</p>
<p>接着看 registerContext 方法</p>
<pre><code class="language-java">/**
* Register context.
*/
private void registerContext(Context context) {

    String contextPath = context.getPath();
    if ("/".equals(contextPath)) {
      contextPath = "";
    }
    Host host = (Host)context.getParent();

    WebResourceRoot resources = context.getResources();
    String[] welcomeFiles = context.findWelcomeFiles();
    List&lt;WrapperMappingInfo&gt; wrappers = new ArrayList&lt;&gt;();

    for (Container container : context.findChildren()) {
      prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);

      if(log.isDebugEnabled()) {
            log.debug(sm.getString("mapperListener.registerWrapper",
                  container.getName(), contextPath, service));
      }
    }

    mapper.addContextVersion(host.getName(), host, contextPath,
            context.getWebappVersion(), context, welcomeFiles, resources,
            wrappers);

    if(log.isDebugEnabled()) {
      log.debug(sm.getString("mapperListener.registerContext",
                contextPath, service));
    }
}
</code></pre>
<p>registerContext 里先获取一些对象,比如 WebResourceRoot 对象、WrapperMappingInfo 对象,然后调用 mapper.addContextVersion。</p>
<p>Mapper#addContextVersion 方法比较琐细,就不细讲了。</p>
<p>其主要逻辑是将 Context 对象,以及 Context 的子容器 Wrapper 对象,每一个都分别构建一个对应的 MappedContext 和 MappedWrapper 对象,</p>
<p>然后把 MappedContext 和 MappedWrapper 塞进 ContextVersion 对象中,</p>
<p>最后把 Context 和 ContextVersion 的对应关系放在 Mapper 对象的一个 Map 里。</p>
<p>这里的 MappedContext 和 MappedWrapper 在 Tomcat 处理 Http 请求的时候是比较关键的。</p>
<p>registerHost 最后再更新了一下可能发生改变里的的 defaultHost。</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top</p><br><br>
来源:https://www.cnblogs.com/sevencoding/p/19927932
頁: [1]
查看完整版本: Tomcat Service的设计和实现:StandardService