> 文章列表 > Tomcat源码:StandServer与StandService

Tomcat源码:StandServer与StandService

Tomcat源码:StandServer与StandService

参考资料:

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

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

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

《Tomcat - Service的设计和实现: 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.javaprotected void initInternal() throws LifecycleException {// 调用父类LifecycleMBeanBase中的实现super.initInternal();// 其余代码}

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

   // LifecycleMBeanBase.javaprivate 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.javaprotected 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对象到MBeanServeronameStringCache = 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 {// 其余代码// 初始化servicefor (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 {// 设置状态为STARTEDsetStateInternal(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();// 启动servicesynchronized (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.javaprotected final MapperListener mapperListener = new MapperListener(this);// MapperListener.javaprivate 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();// 找出defaultHostString 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 wrappersregisterHost(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 对象逐层地处理请求的。