Skip to content

上文讲了Server的具体实现了,本文主要讲Service的设计和实现;我们从上文其实已经知道Server中包含多个service了。@anarkh

  • Tomcat - Service的设计和实现: StandardService
  • 理解思路
  • Service结构设计
  • server.xml
  • Service中的接口设计
  • StandardService的实现
  • Engine相关
  • Connectors相关
  • Executor相关
  • Lifecycle相关模板方法
  • 补充下MapperListener
  • 参考文章

理解思路

  • 第一:类比StandardServer, 抓住StandardService整体类依赖结构来理解

  • 第二:结合server.xml中service配置来理解

见下文具体阐述。

  • 第三:结合Service Config官方配置文档

http://tomcat.apache.org/tomcat-9.0-doc/config/service.html

Service结构设计

我们需要从高一点的维度去理解service的结构设计,而不是多少方法多少代码;这里的理解一定是要结合Server.xml中service配置部分对应理解。@anarkh

server.xml

  • 首先要看下server.xml中Service的配置,这样你便知道了需要了解的4个部分
xml
<Service name="Catalina">


    
    

    
    <Connector port="80" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />
    


    <Connector port="8009" enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
    
	
    
    <Engine name="Catalina" defaultHost="localhost">
    
    </Engine>
  </Service>

Service中的接口设计

  • 公共属性 , name等
java
public String getName();


public void setName(String name);
  • 父Server相关
java
public Server getServer();


public void setServer(Server server);


public ClassLoader getParentClassLoader();


public void setParentClassLoader(ClassLoader parent);


public String getDomain();
  • Connector相关
java
public void addConnector(Connector connector);


public Connector[] findConnectors();


public void removeConnector(Connector connector);
  • Engine
java
public Engine getContainer();


public void setContainer(Engine engine);
  • Excutor相关
java
public void addExecutor(Executor ex);


public Executor[] findExecutors();


public Executor getExecutor(String name);


public void removeExecutor(Executor ex);

StandardService的实现

属性和父Server相关比较简单,这里主要看下其它的方法:

Engine相关

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(); 
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.engine.startFailed"), e);
            }
        }
        
        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);
            }
        }
    }

    
    support.firePropertyChange("container", oldEngine, this.engine);
}

Connectors相关

java
protected Connector connectors[] = new Connector[0];
private final Object connectorsLock = new Object();


@Override
public void addConnector(Connector connector) {

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

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

    
    support.firePropertyChange("connector", null, connector);
}


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


@Override
public Connector[] findConnectors() {
    return connectors;
}


@Override
public void removeConnector(Connector connector) {

    synchronized (connectorsLock) {
        
        int j = -1;
        for (int i = 0; i < connectors.length; i++) {
            if (connector == connectors[i]) {
                j = i;
                break;
            }
        }
        if (j < 0)
            return;
        if (connectors[j].getState().isAvailable()) {
            try {
                connectors[j].stop(); 
            } catch (LifecycleException e) {
                log.error(sm.getString(
                        "standardService.connector.stopFailed",
                        connectors[j]), e);
            }
        }
        connector.setService(null); 
        int k = 0;
        Connector results[] = new Connector[connectors.length - 1];
        for (int i = 0; i < connectors.length; i++) {
            if (i != j)
                results[k++] = connectors[i]; 
        }
        connectors = results;

        
        support.firePropertyChange("connector", connector, null);
    }
}

Executor相关

CRUD方法,代码比较简单

java
@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);
                }
            }
        }
    }
}


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



@Override
public Executor getExecutor(String executorName) {
    synchronized (executors) {
        for (Executor executor: executors) {
            if (executorName.equals(executor.getName()))
                return executor;
        }
    }
    return null;
}


@Override
public void removeExecutor(Executor ex) {
    synchronized (executors) {
        if ( executors.remove(ex) && getState().isAvailable() ) {
            try {
                ex.stop(); 
            } catch (LifecycleException e) {
                log.error(sm.getString("standardService.executor.stop"), e);
            }
        }
    }
}

Lifecycle相关模板方法

首先看 initInternal 方法

java
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

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

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

    
    mapperListener.init();

    
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();
        }
    }
}

initInternal 代码很短,思路也很清晰,就是依次调用了这个成员变量的 init 方法

java
engine.init() 
executor.init 
mapperListener.init()
connector.init()

startInternal 方法

java
@Override
protected void startInternal() throws LifecycleException {

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

    
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }

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

    mapperListener.start();

    
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }
}

startInternal 跟 initInternal 方法一样,也是依次调用

java
engine.start();
executor.start();
mapperListener.start();
connector.start();

补充下MapperListener

mapperListener 的作用是在 start 的时候将容器类对象注册到 Mapper 对象中。

java
public MapperListener(Service service) {
    this.service = service;
    this.mapper = service.getMapper();
}
service.getMapper() 返回的是 StandardService 对象的 mapper 成员变量。


protected final Mapper mapper = new Mapper();

Mapper是 Tomcat 处理 Http 请求时非常重要的组件。Tomcat 使用 Mapper 来处理一个 Request 到 Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。

MapperListener 也是继承自 LifecycleMBeanBase,不过没有重载 initInternal 方法。

  • startInternal 方法
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())) {
            
            registerHost(host);
        }
    }
}
  • findDefaultHost() 方法

首先看 findDefaultHost() 方法

java
private void findDefaultHost() {

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

    boolean found = false;

    if (defaultHost != null && defaultHost.length() > 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));
    }
}

findDefaultHost() 是主要是找出 defaultHost ,并调用 mapper.setDefaultHostName(defaultHost); 这个 defaultHost 是 server.xml 的 <Engine> 标签的属性,一般都是 "localHost"。

从上面代码 for 代码块里可以看出,Host 是 Engine 的子 Container。for 语句就是找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。

  • addListeners(engine) 方法
java
private void addListeners(Container container) {
    container.addContainerListener(this);
    container.addLifecycleListener(this);
    for (Container child : container.findChildren()) {
        addListeners(child);
    }
}

这个方法的作用是,将 MapperListener 这个监听器添加到 Engine 及其子容器中

  • registerHost 调用 registerHost方法来注册 Engine 的字容器 Host。
java
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);
        }
    }

    
    findDefaultHost();

    if(log.isDebugEnabled()) {
        log.debug(sm.getString("mapperListener.registerHost",
                host.getName(), domain, service));
    }
}

registerHost 方法先调用 mapper.addHost,然后调用 registerContext 方法注册 Host 的子容器 Context。 mapper.addHost 方法是将 Host 加入的 Mapper 类的的成员变量MappedHost[] hosts 中。

接着看 registerContext 方法

java
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<>();

    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));
    }
}

registerContext 里先获取一些对象,比如 WebResourceRoot 对象、WrapperMappingInfo 对象,然后调用 mapper.addContextVersion。

Mapper#addContextVersion 方法比较琐细,就不细讲了。

其主要逻辑是将 Context 对象,以及 Context 的子容器 Wrapper 对象,每一个都分别构建一个对应的 MappedContext 和 MappedWrapper 对象,

然后把 MappedContext 和 MappedWrapper 塞进 ContextVersion 对象中,

最后把 Context 和 ContextVersion 的对应关系放在 Mapper 对象的一个 Map 里。

这里的 MappedContext 和 MappedWrapper 在 Tomcat 处理 Http 请求的时候是比较关键的。

registerHost 最后再更新了一下可能发生改变里的的 defaultHost。

参考文章

https://segmentfault.com/a/1190000022026318