当前位置:   article > 正文

Tomcat源码:StandardServer与StandardService_standservice

standservice

前文:

《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使用模板模式将初始化的具体操作留给了每个容器自己实现。

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

        1.2、LifecycleMBeanBase#initInternal

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

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

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

  1. // LifecycleMBeanBase.java
  2. private ObjectName oname = null;
  3. protected MBeanServer mserver = null;
  4. protected void initInternal() throws LifecycleException {
  5. if (oname == null) {
  6. mserver = Registry.getRegistry(null, null).getMBeanServer();
  7. oname = register(this, getObjectNameKeyProperties());
  8. }
  9. }
  10. protected final ObjectName register(Object obj,
  11. String objectNameKeyProperties) {
  12. StringBuilder name = new StringBuilder(getDomain());
  13. name.append(':');
  14. name.append(objectNameKeyProperties);
  15. ObjectName on = null;
  16. try {
  17. on = new ObjectName(name.toString());
  18. Registry.getRegistry(null, null).registerComponent(obj, on, null);
  19. } catch (Exception e) {
  20. log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name), e);
  21. }
  22. return on;
  23. }
  24. // StandardServer.java
  25. protected String getObjectNameKeyProperties() {
  26. return "type=Server";
  27. }

        1.3、StandServer#initInternal

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

  1. protected void initInternal() throws LifecycleException {
  2. // 调用父类LifecycleMBeanBase中的实现
  3. super.initInternal();
  4. // 注册StringCache对象和 MBeanFactory对象到MBeanServer
  5. onameStringCache = register(new StringCache(), "type=StringCache");
  6. MBeanFactory factory = new MBeanFactory();
  7. factory.setContainer(this);
  8. onameMBeanFactory = register(factory, "type=MBeanFactory");
  9. globalNamingResources.init();
  10. // 其余代码
  11. }

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

  1. private NamingResourcesImpl globalNamingResources = null;
  2. public StandardServer() {
  3. super();
  4. globalNamingResources = new NamingResourcesImpl();
  5. globalNamingResources.setContainer(this);
  6. if (isUseNaming()) {
  7. namingContextListener = new NamingContextListener();
  8. addLifecycleListener(namingContextListener);
  9. } else {
  10. namingContextListener = null;
  11. }
  12. }

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

  1. protected void initInternal() throws LifecycleException {
  2. // 其余代码
  3. // 添加可以加载jar包的路径
  4. if (getCatalina() != null) {
  5. ClassLoader cl = getCatalina().getParentClassLoader();
  6. while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
  7. if (cl instanceof URLClassLoader) {
  8. URL[] urls = ((URLClassLoader) cl).getURLs();
  9. for (URL url : urls) {
  10. if (url.getProtocol().equals("file")) {
  11. try {
  12. File f = new File(url.toURI());
  13. if (f.isFile() && f.getName().endsWith(".jar")) {
  14. ExtensionValidator.addSystemResource(f);
  15. }
  16. } catch (URISyntaxException | IOException e) {
  17. }
  18. }
  19. }
  20. }
  21. cl = cl.getParent();
  22. }
  23. }
  24. // 其余代码
  25. }

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

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

  1. protected void initInternal() throws LifecycleException {
  2. // 其余代码
  3. // 初始化service
  4. for (Service service : services) {
  5. service.init();
  6. }
  7. }

        2、Start

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

        2.1、LifecycleBase#start

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

  1. public final synchronized void start() throws LifecycleException {
  2. // `STARTING_PREP`、`STARTING`和`STARTED时,将忽略start()逻辑
  3. if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
  4. LifecycleState.STARTED.equals(state)) {
  5. return;
  6. }
  7. // `NEW`状态时,执行init()方法
  8. if (state.equals(LifecycleState.NEW)) {
  9. init();
  10. }
  11. // `FAILED`状态时,执行stop()方法
  12. else if (state.equals(LifecycleState.FAILED)) {
  13. stop();
  14. }
  15. // 不是`INITIALIZED`和`STOPPED`时,则说明是非法的操作
  16. else if (!state.equals(LifecycleState.INITIALIZED) &&
  17. !state.equals(LifecycleState.STOPPED)) {
  18. invalidTransition(Lifecycle.BEFORE_START_EVENT);
  19. }
  20. try {
  21. // start前的状态设置
  22. setStateInternal(LifecycleState.STARTING_PREP, null, false);
  23. // start逻辑,抽象方法,由组件自行实现
  24. startInternal();
  25. // start过程中,可能因为某些原因失败,这时需要stop操作
  26. if (state.equals(LifecycleState.FAILED)) {
  27. stop();
  28. } else if (!state.equals(LifecycleState.STARTING)) {
  29. invalidTransition(Lifecycle.AFTER_START_EVENT);
  30. } else {
  31. // 设置状态为STARTED
  32. setStateInternal(LifecycleState.STARTED, null, false);
  33. }
  34. } catch (Throwable t) {
  35. ExceptionUtils.handleThrowable(t);
  36. setStateInternal(LifecycleState.FAILED, null, false);
  37. throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
  38. }
  39. }

        2.2、StandardServer#start

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

  1. protected void startInternal() throws LifecycleException {
  2. fireLifecycleEvent(CONFIGURE_START_EVENT, null);
  3. setState(LifecycleState.STARTING);
  4. globalNamingResources.start();
  5. // 启动service
  6. synchronized (servicesLock) {
  7. for (Service service : services) {
  8. service.start();
  9. }
  10. }
  11. }

        本节介绍了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属性。

  1. <!--
  2. 每个Service元素只能有一个Engine元素.元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求
  3. -->
  4. <Service name="Catalina">
  5. <!-- 1. 属性说明
  6. name:Service的名称
  7. -->
  8. <!--2. 一个或多个excecutors -->
  9. <!--
  10. <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
  11. maxThreads="150" minSpareThreads="4"/>
  12. -->
  13. <!--
  14. 3.Connector元素:
  15. 由Connector接口定义.<Connector>元素代表与客户程序实际交互的组件,它负责接收客户请求,以及向客户返回响应结果.
  16. -->
  17. <Connector port="80" maxHttpHeaderSize="8192"
  18. maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
  19. enableLookups="false" redirectPort="8443" acceptCount="100"
  20. connectionTimeout="20000" disableUploadTimeout="true" />
  21. <!-- 属性说明
  22. port:服务器连接器的端口号,该连接器将在指定端口侦听来自客户端的请求。
  23. enableLookups:如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名;
  24. 若为false则不进行DNS查询,而是返回其ip地址。
  25. redirectPort:服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号。
  26. acceptCount:当所有可以使用的处理请求的线程都被用光时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,而返回Connection refused错误。
  27. connectionTimeout:等待超时的时间数(以毫秒为单位)。
  28. maxThreads:设定在监听端口的线程的最大数目,这个值也决定了服务器可以同时响应客户请求的最大数目.默认值为200。
  29. protocol:必须设定为AJP/1.3协议。
  30. address:如果服务器有两个以上IP地址,该属性可以设定端口监听的IP地址,默认情况下,端口会监听服务器上所有IP地址。
  31. minProcessors:服务器启动时创建的处理请求的线程数,每个请求由一个线程负责。
  32. maxProcessors:最多可以创建的处理请求的线程数。
  33. minSpareThreads:最小备用线程 。
  34. maxSpareThreads:最大备用线程。
  35. debug:日志等级。
  36. disableUploadTimeout:禁用上传超时,主要用于大数据上传时。
  37. -->
  38. <Connector port="8009" enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
  39. <!-- 负责和其他HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时就需要用到这个连接器。 -->
  40. <!--
  41. 4. Engine
  42. -->
  43. <Engine name="Catalina" defaultHost="localhost">
  44. </Engine>
  45. </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方法。

  1. protected void initInternal() throws LifecycleException {
  2. super.initInternal();
  3. if (engine != null) {
  4. engine.init();
  5. }
  6. for (Executor executor : findExecutors()) {
  7. if (executor instanceof JmxEnabled) {
  8. ((JmxEnabled) executor).setDomain(getDomain());
  9. }
  10. executor.init();
  11. }
  12. mapperListener.init();
  13. synchronized (connectorsLock) {
  14. for (Connector connector : connectors) {
  15. try {
  16. connector.init();
  17. } catch (Exception e) {
  18. String message = sm.getString("standardService.connector.initFailed", connector);
  19. log.error(message, e);
  20. if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
  21. throw new LifecycleException(message);
  22. }
  23. }
  24. }
  25. }
  26. }

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

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

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

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

  1. public Executor[] findExecutors() {
  2. synchronized (executors) {
  3. return executors.toArray(new Executor[0]);
  4. }
  5. }

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

  1. // StandardService.java
  2. protected final MapperListener mapperListener = new MapperListener(this);
  3. // MapperListener.java
  4. private final Mapper mapper;
  5. private final Service service;
  6. public MapperListener(Service service) {
  7. this.service = service;
  8. this.mapper = service.getMapper();
  9. }

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

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

        2、Start

        2.1、StandardService#startInternal

  1. protected void startInternal() throws LifecycleException {
  2. setState(LifecycleState.STARTING);
  3. if (engine != null) {
  4. synchronized (engine) {
  5. engine.start();
  6. }
  7. }
  8. synchronized (executors) {
  9. for (Executor executor : executors) {
  10. executor.start();
  11. }
  12. }
  13. mapperListener.start();
  14. synchronized (connectorsLock) {
  15. for (Connector connector : connectors) {
  16. try {
  17. if (connector.getState() != LifecycleState.FAILED) {
  18. connector.start();
  19. }
  20. } catch (Exception e) {
  21. log.error(sm.getString("standardService.connector.startFailed", connector), e);
  22. }
  23. }
  24. }
  25. }

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

  1. engine.start();
  2. executor.start();
  3. mapperListener.start();
  4. connector.start();

        2.2、 MapperListener#startInternal

  1. public void startInternal() throws LifecycleException {
  2. setState(LifecycleState.STARTING);
  3. Engine engine = service.getContainer();
  4. if (engine == null) {
  5. return;
  6. }
  7. findDefaultHost();
  8. addListeners(engine);
  9. Container[] conHosts = engine.findChildren();
  10. for (Container conHost : conHosts) {
  11. Host host = (Host) conHost;
  12. if (!LifecycleState.NEW.equals(host.getState())) {
  13. registerHost(host);
  14. }
  15. }
  16. }

        2.3、findDefaultHost

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

  1. private void findDefaultHost() {
  2. Engine engine = service.getContainer();
  3. // 找出defaultHost
  4. String defaultHost = engine.getDefaultHost();
  5. boolean found = false;
  6. if (defaultHost != null && defaultHost.length() > 0) {
  7. Container[] containers = engine.findChildren();
  8. for (Container container : containers) {
  9. Host host = (Host) container;
  10. if (defaultHost.equalsIgnoreCase(host.getName())) {
  11. found = true;
  12. break;
  13. }
  14. String[] aliases = host.findAliases();
  15. for (String alias : aliases) {
  16. if (defaultHost.equalsIgnoreCase(alias)) {
  17. found = true;
  18. break;
  19. }
  20. }
  21. }
  22. }
  23. if (found) {
  24. mapper.setDefaultHostName(defaultHost);
  25. } else {
  26. log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
  27. }
  28. }

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

  1. private void addListeners(Container container) {
  2. container.addContainerListener(this);
  3. container.addLifecycleListener(this);
  4. for (Container child : container.findChildren()) {
  5. addListeners(child);
  6. }
  7. }

        2.3、registerHost 

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

  1. Container[] conHosts = engine.findChildren();
  2. for (Container conHost : conHosts) {
  3. Host host = (Host) conHost;
  4. if (!LifecycleState.NEW.equals(host.getState())) {
  5. // Registering the host will register the context and wrappers
  6. registerHost(host);
  7. }
  8. }

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

  1. private void registerHost(Host host) {
  2. String[] aliases = host.findAliases();
  3. mapper.addHost(host.getName(), aliases, host);
  4. for (Container container : host.findChildren()) {
  5. if (container.getState().isAvailable()) {
  6. registerContext((Context) container);
  7. }
  8. }
  9. findDefaultHost();
  10. if (log.isDebugEnabled()) {
  11. log.debug(sm.getString("mapperListener.registerHost", host.getName(), domain, service));
  12. }
  13. }

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

  1. private void registerContext(Context context) {
  2. String contextPath = context.getPath();
  3. if ("/".equals(contextPath)) {
  4. contextPath = "";
  5. }
  6. Host host = (Host) context.getParent();
  7. WebResourceRoot resources = context.getResources();
  8. String[] welcomeFiles = context.findWelcomeFiles();
  9. List<WrapperMappingInfo> wrappers = new ArrayList<>();
  10. for (Container container : context.findChildren()) {
  11. prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
  12. if (log.isDebugEnabled()) {
  13. log.debug(sm.getString("mapperListener.registerWrapper", container.getName(), contextPath, service));
  14. }
  15. }
  16. mapper.addContextVersion(host.getName(), host, contextPath, context.getWebappVersion(), context, welcomeFiles,
  17. resources, wrappers);
  18. if (log.isDebugEnabled()) {
  19. log.debug(sm.getString("mapperListener.registerContext", contextPath, service));
  20. }
  21. }

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

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

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

        上面的分析中提到了 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》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/741612
推荐阅读
相关标签
  

闽ICP备14008679号