Tomcat源码解析-组件之StandardEngine

1 Container容器

Container容器用来表示tomcat中servlet容器,负责servelt的加载和管理,处理请求,并返回标准的 ServletResponse 对象给连接器。

image

Container容器组件

tomcat 将Container容器按功能分为4个组件,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。

  • Wrapper:表示一个 Servlet

  • Context:表示一个 Web 应用程序,一个 Web 应用程序中可能会有多个 Servlet

  • Host:表示的是一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序

  • Engine:表示引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。

image

2 StandardEngine类结构

image
2.1 Lifecycle接口
public interface Lifecycle {
    ....
    // 初始化方法
    public void init() throws LifecycleException;
    // 启动方法
    public void start() throws LifecycleException;
    // 停止方法,和start对应
    public void stop() throws LifecycleException;
    // 销毁方法,和init对应
    public void destroy() throws LifecycleException;
    // 获取生命周期状态
    public LifecycleState getState();
    // 获取字符串类型的生命周期状态
    public String getStateName();
}

Lifecycle接口定义tomcat中所有组件的生命周期相关接口方法。Tomcat 定义一个基类LifecycleBase 来实现 Lifecycle 接口,把一些公共的逻辑放到基类中实现。而子类就负责实现自己的初始化、启动和停止等模板方法。

详见 Tomcat架构设计-组件生命周期 Lifecycle

2.2 Container接口
public interface Container extends Lifecycle {
    
    public static final String ADD_CHILD_EVENT = "addChild";

    public static final String ADD_VALVE_EVENT = "addValve";

    public static final String REMOVE_CHILD_EVENT = "removeChild";
    
    public static final String REMOVE_VALVE_EVENT = "removeValve";
    
    //返回日志组件
    public Log getLogger();
    
    //返回日志名称
    public String getLogName();
    
    //返回容器注册到JMX bean ObjectName
    public ObjectName getObjectName();
    
    //返回容器注册到JMX bean 命名空间
    public String getDomain();
    
    //返回容器注册到JMX bean 属性
    public String getMBeanKeyProperties();
    
    //返回容器依赖Pipeline组件
    public Pipeline getPipeline();
    
    //返回容器依赖Cluster组件
    public Cluster getCluster();
    
    //设置容器依赖Cluster组件
    public void setCluster(Cluster cluster);
    
    //返回周期性任务执行间隔事件
    public int getBackgroundProcessorDelay();
    
    //设置周期性任务执行间隔事件
    public void setBackgroundProcessorDelay(int delay);
    
    //返回容器名称
    public String getName();
    
    //设置容器名称
    public void setName(String name);
    
    //返回父容器
    public Container getParent();
    
    //设置父容器
    public void setParent(Container container);
    
    //返回父类加载器
    public ClassLoader getParentClassLoader();
    
    //设置父类加载器
    public void setParentClassLoader(ClassLoader parent);
    
    //返回容器依赖Realm组件
    public Realm getRealm();
    
    // 设置容器依赖Realm组件
    public void setRealm(Realm realm);
    
    //容器默认周期性任务处理调用方法
    public void backgroundProcess();
    
    //为当前容器组件添加子容器组件
    public void addChild(Container child);
    
    //添加容器事件监听器
    public void addContainerListener(ContainerListener listener);
    
    //添加属性变更监听器
    public void addPropertyChangeListener(PropertyChangeListener listener);
    
    //查找指定名称的子容器
    public Container findChild(String name);
    
    //获取所有子容器组件
    public Container[] findChildren();
    
    //返回所有容器事件监听器
    public ContainerListener[] findContainerListeners();

    //删除子容器
    public void removeChild(Container child);
    
    //当前容器删除容器事件监听器
    public void removeContainerListener(ContainerListener listener);
    
    //当前容器删除属性变更监听器
    public void removePropertyChangeListener(PropertyChangeListener listener);
    
    //处理容器事件
    public void fireContainerEvent(String type, Object data);
    
    //使用AccessLog组件打印请求日志
    public void logAccess(Request request, Response response, long time,
            boolean useDefault);
    
    //返回访问日志组件AccessLog
    public AccessLog getAccessLog();
    
    //返回设置处理子容器启动关闭线程池核心线程数。
    public int getStartStopThreads();

    //设置处理子容器启动关闭线程池核心线程数。
    public void setStartStopThreads(int startStopThreads);

    //返回tomcat工作目录
    public File getCatalinaBase();

    //返回tomcat安装目录
    public File getCatalinaHome();
}

Container接口定义tomcat中所有容器组件的通用接口方法。Tomcat 定义一个基类ContainerBase 来实现Container 接口,把一些公共的逻辑放到基类中实现。

详见 Tomcat架构设计-容器组件基类 ContainerBase

3 StandardEngine职责&核心属性

3.1 职责

StandardEngine核心职责实现Engine接口,并管理子容器Host组件(下图蓝色),以及从父类ContainerBase(下图红色),LifecycleBase(下图黄色) 继承的通用组件。

image
public interface Engine extends Container {
    
    //获取默认host组件名称
    public String getDefaultHost();
    
    //设置默认host组件名称
    public void setDefaultHost(String defaultHost);
    
    //获取Tomcat实例的JVM路由ID。所有路由ID必须唯一
    public String getJvmRoute();
    
    //设置Tomcat实例的JVM路由ID。所有路由ID必须唯一
    public void setJvmRoute(String jvmRouteId);
    
    //返回关联上层组件Service
    public Service getService();
    
    //设置关联上层组件Service
    public void setService(Service service);
}
3.2 核心属性

StandardEngine内部只定义实现Engine接口所需要的属性。

public class StandardEngine extends ContainerBase implements Engine {

     //默认host子组件名称
    private String defaultHost = null;

     //上层Service组件
    private Service service = null;

     //Tomcat实例的JVM路由ID。所有路由ID必须唯一,用于集群环境中
    private String jvmRouteId;
    
    ...省略代码

其主要属性从ContainerBase继承而来

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
        
    ...省略代码    
    /**
     * 当前容器的名称
     */
    protected String name = null;

    /**
     * 当前容器组件的父容器组件
     */
    protected Container parent = null;

    /**
     * 父类加载器
     */
    protected ClassLoader parentClassLoader = null;


    /**
     * 存储子容器的Map,key表示容器的名称,value表示容器组件对象
     */
    protected final HashMap<String, Container> children = new HashMap<>();

    /**
     * 容器事件监听器
     */
    protected final List<ContainerListener> listeners = new CopyOnWriteArrayList<>();

    /**
     * log组件
     */
    protected Log logger = null;

    /**
     * 关联的log名称
     */
    protected String logName = null;

    /**
     * 当前容器组件对应cluster组件
     */
    protected Cluster cluster = null;

    /**
     * 集群对象CLuster读写锁
     */
    private final ReadWriteLock clusterLock = new ReentrantReadWriteLock();

    /**
     * 当前容器组件对应的的Realm组件
     */
    private volatile Realm realm = null;

    /**
     * 当前容器组件对应的Realm组件读写锁
     */
    private final ReadWriteLock realmLock = new ReentrantReadWriteLock();

    /**
     * 当前容器组件对应的pipeline组件
     */
    protected final Pipeline pipeline = new StandardPipeline(this);

    /**
     * 当前容器组件对应的AccessLog组件
     */
    protected volatile AccessLog accessLog = null;

    /**
     * 获取AccessLog组件时
     * 如果accessLogScanComplete为false
     * 对AccessLog组件初始化(扫描pipeline组件中类型为AccessLog的Value,添加到AccessLog组件)
     */
    private volatile boolean accessLogScanComplete = false;


    //工具类
    /**
     * 错误日志管理器
     */
    protected static final StringManager sm =
        StringManager.getManager(Constants.Package);


    /**
     * 属性变更处理器
     */
    protected final PropertyChangeSupport support =
            new PropertyChangeSupport(this);
            
    ...省略代码           

4 StandardEngine运行流程

tomcat中所有组件都需要经历如下流程。

[图片上传失败...(image-43b823-1565851092609)]

4.1 构建StandardEngine

Tomcat使用Digester解析server.xml,Digester是一款用于将xml转换为Java对象的事件驱动型工具,是对SAX的高层次的封装。相对于SAX,Digester可以针对每一个xml标签设置对应的解析规则。详见 Tomcat相关技术-Digester(二)

Tomcat在Catalina组件初始化阶段调用createStartDigester()创建Digester对象,Digester对象内包含解析server.xml规则,接着通过Digester对象解析server.xml实例化StandardEngine,并对部分属性设置值.

server.xml配置

   <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>

解析<Engine>标签及子标签tomcat在2个规则组EngineRuleSetHostRuleSet中定义了解析规则。

       //设置EngineRuleSet作为解析规则组, 解析<Server><Service>子标签
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));

        //设置HostRuleSet作为解析规则组.解析<Server><Service><Engine>子标签
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
    
4.1 EngineRuleSet规则组
4.1.1 解析<Engine>标签
        /** 解析<Engine>标签实例化StandardEngine对象,并push到操作栈中 **/
        digester.addObjectCreate(prefix + "Engine",
                                 "org.apache.catalina.core.StandardEngine",
                                 "className");

        /** 解析<Engine>标签将标签中属性值映射到其实例化对象中**/
        digester.addSetProperties(prefix + "Engine");
        
        
         /** 解析<Engine>标签将操作栈栈顶对象作为次栈顶对象StandardService.setContainer方法调用的参数,设置到StandardServer.container属性中**/
        digester.addSetNext(prefix + "Engine",
                            "setContainer",
                            "org.apache.catalina.Engine");
                            
        /** 解析<Engine>标签,使用LifecycleListenerRule规则,负责给栈顶对象添加一个生命周期监听器. 默认listenerClass为engineConfigClass,
         * 或者在标签指定org.apache.catalina.startup.EngineConfig属性值作为listenerClass**/
        digester.addRule(prefix + "Engine",
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.EngineConfig",
                          "engineConfigClass"));                    

实例化StandardEngine

    /**
     * 实例化StandardEngine
     */
    public StandardEngine() {
        super();
        /** 设置StandardEngineValve作为pipeline组件尾阀门 **/
        pipeline.setBasic(new StandardEngineValve());
        /** 从系统属性jvmRoute值,设置 Tomcat实例的JVM路由ID **/
        try {
            setJvmRoute(System.getProperty("jvmRoute"));
        } catch(Exception ex) {
            log.warn(sm.getString("standardEngine.jvmRouteFail"));
        }
        /** 设置周期任务执行间隔事件 **/
        backgroundProcessorDelay = 10;
    }

设置标签属性映射到StandardEngine对象

<Engine>标签中属性可以查看官方文档 /docs/config/host.htm,这里所有属性在上面的属性中都有说明。

4.1.2 解析<Cluster>标签
        //解析<Server><Service><Engine><Cluster>标签
        /** 解析<Cluster>标签实例化标签中className属性定义的对象,并push到操作栈中 **/
        digester.addObjectCreate(prefix + "Engine/Cluster",
                                 null, // MUST be specified in the element
                                 "className");

        /** 解析<Cluster>标签将标签中属性值映射到其实例化对象中**/
        digester.addSetProperties(prefix + "Engine/Cluster");

        /** 解析<Cluster>标签将操作栈栈顶对象作为次栈顶对象StandardEngine.setCluster方法调用的参数,即设置到StandardEngine.cluster属性中**/
        digester.addSetNext(prefix + "Engine/Cluster",
                            "setCluster",
                            "org.apache.catalina.Cluster");

设置cluster组件

setCluster()方法是从父类ContainerBase继承.

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
        
    ...省略代码    

    @Override
    public void setCluster(Cluster cluster) {

        Cluster oldCluster = null;
        Lock writeLock = clusterLock.writeLock();
        writeLock.lock();
        try {
            /**  获取当前组件原始Cluster组件对象引用设置给oldCluster **/
            oldCluster = this.cluster;
            if (oldCluster == cluster)
                return;
            this.cluster = cluster;

            /**  如果当前组件还在运行,则停止oldCluster组件 **/
            if (getState().isAvailable() && (oldCluster != null) &&
                (oldCluster instanceof Lifecycle)) {
                try {
                    ((Lifecycle) oldCluster).stop();
                } catch (LifecycleException e) {
                    log.error("ContainerBase.setCluster: stop: ", e);
                }
            }

            /** 将新设置cluster组件和当前容器组件对象反相关联 **/
            if (cluster != null)
                cluster.setContainer(this);

            /** 如果当前组件处于运行状态,启动新设置cluster组件 **/
            if (getState().isAvailable() && (cluster != null) &&
                (cluster instanceof Lifecycle)) {
                try {
                    ((Lifecycle) cluster).start();
                } catch (LifecycleException e) {
                    log.error("ContainerBase.setCluster: start: ", e);
                }
            }
        } finally {
            writeLock.unlock();
        }

        /** 触发属性变更 **/
        support.firePropertyChange("cluster", oldCluster, cluster);
    }
4.1.3 解析<Listener>标签
        //解析<Server><Service><Engine><Listener>标签
        /** 解析<Listener>标签实例化标签中className属性定义的对象,并push到操作栈中 **/
        digester.addObjectCreate(prefix + "Engine/Listener",
                                 null, // MUST be specified in the element
                                 "className");

        /** 解析<Listener>标签将标签中属性值映射到其实例化对象中**/
        digester.addSetProperties(prefix + "Engine/Listener");

        /** 解析<Cluster>标签将操作栈栈顶对象作为次栈顶对象StandardEngine.addLifecycleListener方法调用的参数,即设置到StandardEngine生命周期监听器数组中**/
        digester.addSetNext(prefix + "Engine/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

注册生命周期监听器

addLifecycleListener()方法是从父类LifecycleBase继承.


public abstract class LifecycleBase implements Lifecycle {

    /**
     * 管理当前组件生命周期监听器列表
     */
    private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
    
    /**
     * 给当前组件添加一个生命周期监听器
     */
    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycleListeners.add(listener);
    }
4.1.4 解析<Valve>标签
        //解析<Server><Service><Engine><Valve>标签
        /** 解析<Valve>标签实例化标签中className属性定义的对象,并push到操作栈中 **/
        digester.addObjectCreate(prefix + "Engine/Valve",
                                 null, // MUST be specified in the element
                                 "className");

        /** 解析<Valve>标签将标签中属性值映射到其实例化对象中**/
        digester.addSetProperties(prefix + "Engine/Valve");


        /** 解析<Valve>标签将操作栈栈顶对象作为次栈顶对象StandardEngine.addValve方法调用的参数,即设置到StandardEngine中Pipline组件中**/
        digester.addSetNext(prefix + "Engine/Valve",
                            "addValve",
                            "org.apache.catalina.Valve");

给子pipeline组件添加Valve处理器

addValve()方法是从父类ContainerBase继承.

public abstract class ContainerBase extends LifecycleMBeanBase
        implements Container {
        
    /**
     * 当前容器组件对应的pipeline组件
     */
    protected final Pipeline pipeline = new StandardPipeline(this);    
    /**
     * 给pipeline组件添加一个Valve
     */
    public synchronized void addValve(Valve valve) {
        pipeline.addValve(valve);
    }
4.1.5 解析<Realm>标签
    /** 解析<Realm>标签使用自定义规则组RealmRuleSet**/
    digester.addRuleSet(new RealmRuleSet(prefix + "Engine/"));
    
    /**
     * 解析Realm标签添加到StandardEngine属性中
     */
    @SuppressWarnings("deprecation")
    public class RealmRuleSet extends RuleSetBase {
    
        @Override
        public void addRuleInstances(Digester digester) {
            StringBuilder pattern = new StringBuilder(prefix);
            for (int i = 0; i < MAX_NESTED_REALM_LEVELS; i++) {
                if (i > 0) {
                    pattern.append('/');
                }
                pattern.append("Realm");
                addRuleInstances(digester, pattern.toString(), i == 0 ? "setRealm" : "addRealm");
            }
        }
    
        private void addRuleInstances(Digester digester, String pattern, String methodName) {
            digester.addObjectCreate(pattern, null /* MUST be specified in the element */,
                    "className");
            digester.addSetProperties(pattern);
            digester.addSetNext(pattern, methodName, "org.apache.catalina.Realm");
            digester.addRuleSet(new CredentialHandlerRuleSet(pattern + "/"));
        }
    }    
4.2 EngineRuleSet规则组
   @Override
    public void addRuleInstances(Digester digester) {

        //解析<Server><Service><Engine><Host>标签
        /** 解析<Host>标签实例化StandardHost对象,并push到操作栈中 **/
        digester.addObjectCreate(prefix + "Host",
                                 "org.apache.catalina.core.StandardHost",
                                 "className");

        /** 解析<Host>标签将标签中属性值映射到其实例化对象中**/
        digester.addSetProperties(prefix + "Host");


        /** 解析<Host>标签,使用CopyParentClassLoaderRule规则,负责调用次栈顶对象getParentClassLoader获取父类加载,设置到栈顶对象parentClassLoader属性上 **/
        digester.addRule(prefix + "Host",
                         new CopyParentClassLoaderRule());

        /** 解析<Host>标签,使用LifecycleListenerRule规则,负责给栈顶对象添加一个生命周期监听器. 默认为hostConfigClass,或者在标签指定org.apache.catalina.startup.HostConfig属性**/
        digester.addRule(prefix + "Host",
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.HostConfig",
                          "hostConfigClass"));

        /** 解析<Host>标签将操作栈栈顶对象作为次栈顶对象StandardService.addChild方法调用的参数,即将实例化StandardHost对象添加StandardServer.child子容器列表属性中**/
        digester.addSetNext(prefix + "Host",
                            "addChild",
                            "org.apache.catalina.Container");
4.3 重要的规则
4.3.1 CopyParentClassLoaderRule规则

CopyParentClassLoaderRule规则,负责调用次栈顶对象getParentClassLoader获取父类加载,设置到栈顶对象parentClassLoader属性上

public class CopyParentClassLoaderRule extends Rule {


    public CopyParentClassLoaderRule() {
    }
    
    @Override
    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {

        if (digester.getLogger().isDebugEnabled())
            digester.getLogger().debug("Copying parent class loader");
        Container child = (Container) digester.peek(0);
        Object parent = digester.peek(1);
        Method method =
            parent.getClass().getMethod("getParentClassLoader", new Class[0]);
        ClassLoader classLoader =
            (ClassLoader) method.invoke(parent, new Object[0]);
        child.setParentClassLoader(classLoader);

    }
}

4.3.2 LifecycleListenerRule规则

LifecycleListenerRule 规则负责给栈顶对象添加一个生命周期监听器.

/**
 *  解析标签给栈顶对象添加一个生命周期监听器
 */
public class LifecycleListenerRule extends Rule {
    
    public LifecycleListenerRule(String listenerClass, String attributeName) {
        this.listenerClass = listenerClass;
        this.attributeName = attributeName;
    }
    /**
     * 标准中指定属性,用来设置监听器处理类
     */
    private final String attributeName;

    /**
     * 默认监听器处理类
     */
    private final String listenerClass;
    
    @Override
    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {

        /** 获取栈顶原始对象 **/
        Container c = (Container) digester.peek();

        /** 获取次栈顶元素对象 **/
        Container p = null;
        Object obj = digester.peek(1);

        /** 如果栈顶元素对象是容器设置给p **/
        if (obj instanceof Container) {
            p = (Container) obj;
        }

        String className = null;

        /** 获取标签attributeName值赋值给className **/
        if (attributeName != null) {
            String value = attributes.getValue(attributeName);
            if (value != null)
                className = value;
        }

        /** 获取次栈顶对象attributeName属性值赋值给className **/
        if (p != null && className == null) {
            String configClass =
                (String) IntrospectionUtils.getProperty(p, attributeName);
            if (configClass != null && configClass.length() > 0) {
                className = configClass;
            }
        }

        /** 如果className == null使用listenerClass作为className默认值**/
        if (className == null) {
            className = listenerClass;
        }


        /** 实例化className添加栈顶对象生命周期监听器列表中*/
        Class<?> clazz = Class.forName(className);
        LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
        c.addLifecycleListener(listener);
    }
}

组件生命周期

接下来初始化开始则进入tomcat组件的生命周期,对于tomcat中所有组件都必须实现Lifecycle,Tomcat 定义一个基类LifecycleBase 来实现 Lifecycle 接口,把一些公共的逻辑放到基类中实现,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等模板方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上 Internal,叫 initInternal、startInternal 等。

StandardEngine父类对容器的初始化、启动和停止等模板方法进行的了默认实现。子类容器只需要重写父类实现即可实现扩展。

4.2 初始化StandardEngine
    @Override
    protected void initInternal() throws LifecycleException {
        getRealm();
        super.initInternal();
    }
4.3 启动StandardEngine
    @Override
    protected synchronized void startInternal() throws LifecycleException {
        if(log.isInfoEnabled()){
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
        }
        super.startInternal();
    }

StandardEngine其他生命周期实现均从父类ContainerBase继承。

4.4 停止StandardEngine
/**
     * 组件停止模板实现
     */
    @Override
    protected synchronized void stopInternal() throws LifecycleException {

        /** 关闭处理定时任务线程 **/
        threadStop();

        /** 设置当前容器的状态LifecycleState.STOPPING **/
        setState(LifecycleState.STOPPING);


        /** 停止pipeline组件**/
        if (pipeline instanceof Lifecycle &&
                ((Lifecycle) pipeline).getState().isAvailable()) {
            ((Lifecycle) pipeline).stop();
        }

        /** 使用线程池异步处理子容器关闭 **/
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StopChild(children[i])));
        }

        /** 等待所有子容器关闭完毕 **/
        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStopFailed"), e);
                fail = true;
            }
        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStopFailed"));
        }

        /** 关闭Realm组件**/
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).stop();
        }

        /** 关闭Cluster组件**/
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).stop();
        }
    }
4.5 销毁StandardEngine
/**
     * 组件销毁模板实现
     */
    @Override
    protected void destroyInternal() throws LifecycleException {

        /** 销毁Realm组件**/
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).destroy();
        }

        /** 销毁cluster组件**/
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).destroy();
        }

        /** 销毁cluster组件**/
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).destroy();
        }

        /** 清空所有子容器 **/
        for (Container child : findChildren()) {
            removeChild(child);
        }

        /** 父容器和当前容器组件取消关联 **/
        if (parent != null) {
            parent.removeChild(this);
        }

        /** 关闭处理子容器启动关闭线程池 **/
        if (startStopExecutor != null) {
            startStopExecutor.shutdownNow();
        }

        super.destroyInternal();
    }

5 核心方法

5.1 重写addChild
    @Override
    public void addChild(Container child) {
        if (!(child instanceof Host)){
            throw new IllegalArgumentException
                    (sm.getString("standardEngine.notHost"));
        }
        super.addChild(child);
    }
5.2 重写logAccess
    /**
     * AccessLog组件
     */
    private final AtomicReference<AccessLog> defaultAccessLog =
        new AtomicReference<>();
        
    
    /**
     * 使用AccessLog组件打印日志,
     *
     * 如果useDefault=true,使用defaultAccessLog属性中保存AccessLog打印日志
     * 如果defaultAccessLog未初始化AccessLog,则尝试从子容器host,context获取绑定的AccessLog组件设置到defaultAccessLog,使用新设置AccessLog组件打印日志
     */
    @Override
    public void logAccess(Request request, Response response, long time,
            boolean useDefault) {

        boolean logged = false;

        if (getAccessLog() != null) {
            accessLog.log(request, response, time);
            logged = true;
        }

        if (!logged && useDefault) {
            AccessLog newDefaultAccessLog = defaultAccessLog.get();
            /** 如果newDefaultAccessLog未初始化 **/
            if (newDefaultAccessLog == null) {
                /** 获取默认host子容器组件 **/
                Host host = (Host) findChild(getDefaultHost());
                Context context = null;
                if (host != null && host.getState().isAvailable()) {
                    /** 获取host容器绑定的AccessLog 设置给 newDefaultAccessLog**/
                    newDefaultAccessLog = host.getAccessLog();

                    if (newDefaultAccessLog != null) {
                        if (defaultAccessLog.compareAndSet(null,
                                newDefaultAccessLog)) {
                            AccessLogListener l = new AccessLogListener(this,
                                    host, null);
                            l.install();
                        }
                    } else {
                        /** 获取context容器绑定的AccessLog 设置给 newDefaultAccessLog**/
                        context = (Context) host.findChild("");
                        if (context != null &&
                                context.getState().isAvailable()) {
                            newDefaultAccessLog = context.getAccessLog();
                            if (newDefaultAccessLog != null) {
                                if (defaultAccessLog.compareAndSet(null,
                                        newDefaultAccessLog)) {
                                    /** 并设置AccessLogListener 作为当前容器对象engine,和子容器host的监听器。 **/
                                    AccessLogListener l = new AccessLogListener(
                                            this, null, context);
                                    l.install();
                                }
                            }
                        }
                    }
                }

                /** 如果无法从子容器获取AccessLog,默认使用NoopAccessLog作为默认的AccessLog **/
                if (newDefaultAccessLog == null) {
                    newDefaultAccessLog = new NoopAccessLog();
                    if (defaultAccessLog.compareAndSet(null,
                            newDefaultAccessLog)) {
                        /** 并设置AccessLogListener 作为当前容器对象engine,和子容器host的监听器。 **/
                        AccessLogListener l = new AccessLogListener(this, host,
                                context);
                        l.install();
                    }
                }
            }
            /** 使用newDefaultAccessLog打印日志 **/
            newDefaultAccessLog.log(request, response, time);
        }
    }    

AccessLogListener

AccessLogListener同时实现3种监听器

   protected static final class AccessLogListener
            implements PropertyChangeListener, LifecycleListener,
            ContainerListener {

绑定监听器,取消监听器

       public void install() {
            engine.addPropertyChangeListener(this);
            if (host != null) {
                host.addContainerListener(this);
                host.addLifecycleListener(this);
            }
            if (context != null) {
                context.addLifecycleListener(this);
            }
        }

        private void uninstall() {
            disabled = true;
            if (context != null) {
                context.removeLifecycleListener(this);
            }
            if (host != null) {
                host.removeLifecycleListener(this);
                host.removeContainerListener(this);
            }
            engine.removePropertyChangeListener(this);
        }

监听事件处理

        @Override
        public void lifecycleEvent(LifecycleEvent event) {
            if (disabled) return;

            String type = event.getType();
            if (Lifecycle.AFTER_START_EVENT.equals(type) ||
                    Lifecycle.BEFORE_STOP_EVENT.equals(type) ||
                    Lifecycle.BEFORE_DESTROY_EVENT.equals(type)) {
                engine.defaultAccessLog.set(null);
                uninstall();
            }
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (disabled) return;
            if ("defaultHost".equals(evt.getPropertyName())) {
                engine.defaultAccessLog.set(null);
                uninstall();
            }
        }

        @Override
        public void containerEvent(ContainerEvent event) {
            if (disabled) return;
            if (Container.ADD_CHILD_EVENT.equals(event.getType())) {
                Context context = (Context) event.getData();
                if ("".equals(context.getPath())) {
                    engine.defaultAccessLog.set(null);
                    uninstall();
                }
            }
        }

6 处理流程

每一个容器组件都有一个 Pipeline 对象,Pipeline 中维护了 Valve 链表,默认时每一个Pipeline存放了一个默认的BasicValue,
这里每一个Value表示一个处理点,当调用addValve 方法时会将添加Vaule添加到链表头部,Pipeline 中没有 invoke
方法,请求处理时Pipeline只需要获取链表中第一个Valve调用incoke去执行,执行完毕后当前Value会调用
getNext.invoke() 来触发下一个 Valve 调用

每一个容器在执行到最后一个默认BasicValue时,会负责调用下层容器的 Pipeline 里的第一个 Valve

Pipeline.jpg

对于StandardEngine容器来说默认情况只添加了一个默认的StandardEngineValve。

7.1 StandardEngineValve
final class StandardEngineValve extends ValveBase {

    public StandardEngineValve() {
        super(true);
    }


    private static final StringManager sm =
        StringManager.getManager(Constants.Package);


    @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        /** 获取host子容器组件 **/
        Host host = request.getHost();
        if (host == null) {
            response.sendError
                (HttpServletResponse.SC_BAD_REQUEST,
                 sm.getString("standardEngine.noHost",
                              request.getServerName()));
            return;
        }

        /** 如果当前请求支持异步,则检查当前容器组件Pipeline管道种所有Value阀是否都支持异步,如果不是则重置为false **/
        if (request.isAsyncSupported()) {
            /** getPipeline().isAsyncSupported() 如果当前容器组件Pipeline管道种所有Value阀都支持异步则返回true**/
            /** 设置当前请求是否支持异步,需要当前容器Pipeline管道种所有Value阀都支持异步 **/
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        /** 调用host容器的Pipeline管道的第一个Value阀执行 **/
        host.getPipeline().getFirst().invoke(request, response);
    }
}

7 EngineConfig监听器

解析xml种给StandardEngine组件添加LifecycleListener监听器,负责监听生命周期事件打印日志

public class EngineConfig
    implements LifecycleListener {

    private static final Log log = LogFactory.getLog(EngineConfig.class);

    protected Engine engine = null;

    protected static final StringManager sm =
        StringManager.getManager(Constants.Package);


    @Override
    public void lifecycleEvent(LifecycleEvent event) {


        try {
            /** 获取触发事件的组件engine **/
            engine = (Engine) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("engineConfig.cce", event.getLifecycle()), e);
            return;
        }
        /** 如果当前事件START_EVENT,调用start()方法打印启动日志 **/
        if (event.getType().equals(Lifecycle.START_EVENT))
            start();

        /** 如果当前事件STOP_EVENT,调用stop()方法打印停止日志 **/
        else if (event.getType().equals(Lifecycle.STOP_EVENT))
            stop();
    }

    protected void start() {
        if (engine.getLogger().isDebugEnabled())
            engine.getLogger().debug(sm.getString("engineConfig.start"));
    }


    protected void stop() {
        if (engine.getLogger().isDebugEnabled())
            engine.getLogger().debug(sm.getString("engineConfig.stop"));

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容