Tomcat源码:StandardServer与StandardService

前文:

《Tomcat源码:启动类Bootstrap与Catalina的加载》

《Tomcat源码:容器的生命周期管理与事件监听》

        写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。

前言

        在前文中我们介绍了tomcat启动类的加载,在Catalina初始化时加载了server.xml,并调用了getServer().init()方法加载server接口的实现类standserver。另外还介绍了以standserver为代表的容器组件共同继承的用于管理生命周期的抽象类LifecycleBase。

        本文我们接着前文的内容继续介绍standserver与standservice。

目录

前言

 一、StandardServer

        1、init

        1.1、LifecycleBase#init

        1.2、LifecycleMBeanBase#initInternal

        1.3、StandServer#initInternal

        2、Start

        2.1、LifecycleBase#start

        2.2、StandardServer#start

二、StandService

        1、init

        1.1、StandardService#initInternal

        2、Start

        2.1、StandardService#startInternal

        2.2、 MapperListener#startInternal

        2.3、findDefaultHost

        2.3、registerHost 


 一、StandardServer

        Catalina类的主要作用就是根据server.xml的配置来初始化Tomcat运行所需要的组件,比如 Server,Service 等等,然后调用成员变量Server类对象的init和start方法,来启动 tomcat。
        一个 Server 类的实例就代表了一个 Tomcat 的容器,一个Tomcat 进程只会有一个 Server 实例。Server 是一个接口,它的实现类是 StandardServer

        1、init

        1.1、LifecycleBase#init

        StandardServer的init方法由父类LifecycleBase实现,可以看到LifecycleBase使用模板模式将初始化的具体操作留给了每个容器自己实现。

@Override
public final synchronized void init() throws LifecycleException {
    // 非NEW状态,不允许调用init()方法
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
 
    try {
        // 初始化逻辑之前,先将状态变更为`INITIALIZING`
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        // 初始化,该方法为一个abstract方法,需要组件自行实现
        initInternal();
        // 初始化完成之后,状态变更为`INITIALIZED`
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        // 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED`
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
}

        1.2、LifecycleMBeanBase#initInternal

        先看第一步,调用父类中的initInternal,这个方法的实现我们可以在父类LifecycleMBeanBase中找到

    // StandardServer.java
    protected void initInternal() throws LifecycleException {
        // 调用父类LifecycleMBeanBase中的实现
        super.initInternal();
        
        // 其余代码
    }

         该方法其实就是将当前容器注册到MBeanServer中,相关的知识可以看我之前的文章《Java8之JMX与MBean》,不了解的可以将它当作Spring中的IOC,可以管控容器中的方法调用与生命周期,这里就是将StandServer进行注册。

   // LifecycleMBeanBase.java
   private ObjectName oname = null;
   protected MBeanServer mserver = null; 

   protected void initInternal() throws LifecycleException {
        if (oname == null) {
            mserver = Registry.getRegistry(null, null).getMBeanServer();
            oname = register(this, getObjectNameKeyProperties());
        }
    }

    protected final ObjectName register(Object obj,
            String objectNameKeyProperties) {
        StringBuilder name = new StringBuilder(getDomain());
        name.append(':');
        name.append(objectNameKeyProperties);

        ObjectName on = null;

        try {
            on = new ObjectName(name.toString());
            Registry.getRegistry(null, null).registerComponent(obj, on, null);
        } catch (Exception e) {
            log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name), e);
        }
        return on;
    }

    // StandardServer.java
    protected String getObjectNameKeyProperties() {
        return "type=Server";
    }

        1.3、StandServer#initInternal

        再回到StandServer#initInternal中,调用完父类的initInternal方法后初始化 onameMBeanFactory和onameStringCache这属性,内容其实就是注册StringCache对象和 MBeanFactory对象到MBeanServer,再然后就是调用 globalNamingResources 对象的 init 方法。

    protected void initInternal() throws LifecycleException {
        // 调用父类LifecycleMBeanBase中的实现
        super.initInternal();

        // 注册StringCache对象和 MBeanFactory对象到MBeanServer
        onameStringCache = register(new StringCache(), "type=StringCache");
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");

        globalNamingResources.init();

        // 其余代码
    }

         globalNamingResources为全局命名资源,对应server.xml中的<GlobalNamingResources>,用来定义一些外部访问资源,在StandServer构造方法中创建。

    private NamingResourcesImpl globalNamingResources = null;
    public StandardServer() {
        super();
        globalNamingResources = new NamingResourcesImpl();
        globalNamingResources.setContainer(this);
        if (isUseNaming()) {
            namingContextListener = new NamingContextListener();
            addLifecycleListener(namingContextListener);
        } else {
            namingContextListener = null;
        }
    }

        再往下这段是将catalina的parentClassLoader(这个属性在Bootstrap#init方法里通过反射调用Catalina的setParentClassLoader将sharedClassLoader传进去,也就是说这个 parentClassLoader就是sharedClassLoader指向的对象,也就是一个URLClassLoader对象)里能加载的jar文件的目录,都添加到ExtensionValidator 这个集合中。

    protected void initInternal() throws LifecycleException {
        // 其余代码
        // 添加可以加载jar包的路径
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File(url.toURI());
                                if (f.isFile() && f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException | IOException e) {
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // 其余代码
    }

        最后就是循环创建所有的service对象。Server里的service是在server.xml里定义的,在 Catalina解析server.xml的时候初始化,并注入到Server对象里。

        显然 StandardServer#initInternal() 方法最重要的就是这段,调用 Service#init 方法来创建Service这个Tomcat的核心组件之一。

    protected void initInternal() throws LifecycleException {
        // 其余代码
        // 初始化service
        for (Service service : services) {
            service.init();
        }
    }

        2、Start

        介绍完了server组件的初始化方法init,接着来看启动方法,该流程是在catlina中调用getServer().start()触发的。

        2.1、LifecycleBase#start

       和init方法一样,server的start方法也是由父类LifecycleBase中实现,也同样使用模板模式将具体实现留给了子类Standserver。

public final synchronized void start() throws LifecycleException {
    // `STARTING_PREP`、`STARTING`和`STARTED时,将忽略start()逻辑
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        return;
    }
    // `NEW`状态时,执行init()方法
    if (state.equals(LifecycleState.NEW)) {
        init();
    }
    // `FAILED`状态时,执行stop()方法
    else if (state.equals(LifecycleState.FAILED)) {
        stop();
    }
    // 不是`INITIALIZED`和`STOPPED`时,则说明是非法的操作
    else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
    try {
        // start前的状态设置
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        // start逻辑,抽象方法,由组件自行实现
        startInternal();
        // start过程中,可能因为某些原因失败,这时需要stop操作
        if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            // 设置状态为STARTED
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    }
}

        2.2、StandardServer#start

        方法的第一行代码先触发 CONFIGURE_START_EVENT 事件,以便执行 StandardServer 的 LifecycleListener 监听器,然后调用 setState 方法设置成 LifecycleBase 的 state 属性为 LifecycleState.STARTING。
        接着就 globalNamingResources.start(),跟 initInternal 方法其实是类似的。
 

    protected void startInternal() throws LifecycleException {
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
        globalNamingResources.start();
        // 启动service
        synchronized (servicesLock) {
            for (Service service : services) {
                service.start();
            }
        }
    }

        本节介绍了Server组件,Server组件是tomcat的核心组件之一,它是通过调用init和start方法来启动tomcat的,而Server的init方法和start方法则是调用Service的init和start方法来启动 Service(tomcat的另一个核心组件)。
        可以看出,一个 Tomcat 进程只有一个 Server 实例,一个 Server 实例可以包含多个 Service 对象。

二、StandService

        Service 是Tomcat的核心组件之一,大概可以分为4个部分,service属性、executor属性、connector属性、engine属性。

<!--
    每个Service元素只能有一个Engine元素.元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求
-->

<Service name="Catalina">
<!-- 1. 属性说明
	name:Service的名称
-->

    <!--2. 一个或多个excecutors -->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->

    <!--
		3.Connector元素:
			由Connector接口定义.<Connector>元素代表与客户程序实际交互的组件,它负责接收客户请求,以及向客户返回响应结果.
    -->
    <Connector port="80" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />
    <!-- 属性说明
		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:禁用上传超时,主要用于大数据上传时。
    -->


    <Connector port="8009" enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
    <!-- 负责和其他HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时就需要用到这个连接器。 -->
	
    <!--
		4. Engine
    -->
    <Engine name="Catalina" defaultHost="localhost">
    
    </Engine>
  </Service>

        上文中分析了 Server 类的 init 和 start 方法,其中最核心的内容就是调用了 StandardServer 类的的 Service 类型的成员的 init 和 start 方法。Service 的实现类是StandardService。StandardService和StandardServer一样也是继承自 LifecycleMBeanBase。 

        1、init

        1.1、StandardService#initInternal

        开头和StandServer一样调用了父类的initInternal方法,然后分别调用了四个成员变量engine、executor、mapperListener、connector的init方法。

    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) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString("standardService.connector.initFailed", connector);
                    log.error(message, e);

                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                        throw new LifecycleException(message);
                    }
                }
            }
        }
    }

        先看看这几个成员变量的定义

private Engine engine = null;
protected final ArrayList<Executor> executors = new ArrayList<>();
protected final MapperListener mapperListener = new MapperListener(this);
protected Connector connectors[] = new Connector[0];

        除了mapperListener,都是解析server.xml文件时根据配置文件的配置初始化的。其中engine 和connector也是Tomcat核心组件之二,会分别单独解析,这里就先略过。

        executor的实现类是,StandardThreadExecutor,同样继承自 LifecycleMBeanBase,它的作用跟线程池类似,这部分内容我们会在介绍connect时一并介绍。

    public Executor[] findExecutors() {
        synchronized (executors) {
            return executors.toArray(new Executor[0]);
        }
    }

        mapperListener的作用是在start的时候将容器类对象注册到Mapper对象中,这里我们先来看下他的构造方法。

    // StandardService.java
    protected final MapperListener mapperListener = new MapperListener(this);

    // MapperListener.java
    private final Mapper mapper;
    private final Service service;
    public MapperListener(Service service) {
        this.service = service;
        this.mapper = service.getMapper();
    }

        service.getMapper() 返回的是StandardService对象的mapper成员变量,该变量用来处理 Http 请求。Tomcat使用Mapper来处理一个Request到Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。

        MapperListener不过没有重载 initInternal 方法,因此init只是单纯的注册了下MBean,没有别的操作。

        2、Start

        2.1、StandardService#startInternal

    protected void startInternal() throws LifecycleException {
        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) {
                try {
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString("standardService.connector.startFailed", connector), e);
                }
            }
        }
    }

 startInternal 跟 initInternal 方法一样,也是依次调用。executor 的 start 方法初始化了内部的线程池,具体内容我们后续介绍,这里先看下mapperListener的启动。

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

        2.2、 MapperListener#startInternal

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

        2.3、findDefaultHost

        findDefaultHost()其实就是找出 defaultHost ,并存储到mapper中。这个 defaultHost 是 server.xml 的 <Engine> 标签的属性,一般都是 "localHost"。
        for循环中的代码块总结下就是取出Engine中的子Container(即host)然后找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。

    private void findDefaultHost() {
        Engine engine = service.getContainer();
        // 找出defaultHost
        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));
        }
    }

        addListeners就是将MapperListener这个监听器添加到 Engine 及其子容器中 

    private void addListeners(Container container) {
        container.addContainerListener(this);
        container.addLifecycleListener(this);
        for (Container child : container.findChildren()) {
            addListeners(child);
        }
    }

        2.3、registerHost 

        最后是for循环中的代码,这段的作用就是,调用 registerHost方法来注册 Engine 的字容器 Host。

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

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

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

        registerContext又继续将context下面的每个wrapper与context都添加到mapper

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

         prepareWrapperMappingInfo用于准备注册到mapper下的wrapper,这儿mapper对于wrapper的支持是wrapper的包装对象WrapperMappingInfo。而一个context可能有多个wrapper,所以WrapperMappingInfo是一个list。

        简单来说就是将映射url、wrapper名字和资源只读标记等信息组合成对象添加到wrappers中。

    private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
        String wrapperName = wrapper.getName();
        boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
        String[] mappings = wrapper.findMappings();
        for (String mapping : mappings) {
            boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
            wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
        }
    }

        上面的分析中提到了 Engine、Host、Context、Wrapper 和 Connector。除了 Connector,其余的都是 Container 接口的实现类。它们的父类是 ContainerBase,ContainerBase 继承自 LifecycleMBeanBase,因此 Container 也有生命周期方法。
        每一个 Container 都可能有子Container,其中,Engine的子Container是Host,Host的子 Container是Context,Context 的子Container是Wrapper,这里说的父子关系,不是指类继承关系,而是说一个 Container 内部有一个 Map,这个 Map 保存了其他类型的 Container。

        Container是能够执行客户端请求并且给出响应的对象,Tomcat便是用这些 Container 对象逐层地处理请求的。

 参考资料:

《Tomcat源码解析系列(三)Server》

《Tomcat - Server的设计和实现: StandardServer》

《Tomcat源码解析系列(四)Service》

《Tomcat - Service的设计和实现: StandardService》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值