【Tomcat】三.Tomcat启动(上)

作者: Weidan 分类: Tomcat 发布时间: 2020-04-19

上一篇的时序图:

一. StandardRoot启动

public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {

  @Override
  protected void startInternal() throws LifecycleException {
    mainResources.clear();

    // 根据类型,创建对应的资源包实例
    main = createMainResourceSet();

    mainResources.add(main);

    // 一次启动此类指定的资源
    for (List<WebResourceSet> list : allResources) {
      // Skip class resources since they are started below
      if (list != classResources) {
        for (WebResourceSet webResourceSet : list) {
          webResourceSet.start();
        }
      }
    }

    // 扫描lib下的所有jar包
    processWebInfLib();
    // Need to start the newly found resources
    for (WebResourceSet classResource : classResources) {
      classResource.start();
    }

    cache.enforceObjectMaxSizeLimit();

    setState(LifecycleState.STARTING);
  }

}

那我们知道,一个 webapp 入口是 WEB-INF ,因为里边包含了 web.xml 以及 lib包,所以此时就是扫描 lib 下所有的 jar 包,然后加载到 webappClassLoader 中去。所以这个时候 StandardRoot 做的事情就是加载所有的 jar 包。

二. 发送事件通知加载web应用

上面图我们可以看到,fireLifecycleEvent 通知了 ContextConfig 加载 webapp 的配置、加载所有 Pipeline

先看第一个:

2.1 加载配置

public class ContextConfig implements LifecycleListener {
  protected synchronized void configureStart() {
    // Called from StandardContext.start()

    if (log.isDebugEnabled()) {
      log.debug(sm.getString("contextConfig.start"));
    }

    if (log.isDebugEnabled()) {
      log.debug(sm.getString("contextConfig.xmlSettings",
                             context.getName(),
                             Boolean.valueOf(context.getXmlValidation()),
                             Boolean.valueOf(context.getXmlNamespaceAware())));
    }
    // 配置webConfig,会将项目中的web.xml跟tomcat目录下的web.xml合并
    // 并且加载所有必要的jar包
    webConfig();

    if (!context.getIgnoreAnnotations()) {
      applicationAnnotationsConfig();
    }
    if (ok) {
      // 一堆配置鉴权的东西,tomcat-users.xml配置的东西
      validateSecurityRoles();
    }

    if (ok) {
      authenticatorConfig();
    }

    if (log.isDebugEnabled()) {
      log.debug("Pipeline Configuration:");
      Pipeline pipeline = context.getPipeline();
      Valve valves[] = null;
      if (pipeline != null) {
        valves = pipeline.getValves();
      }
      if (valves != null) {
        for (int i = 0; i < valves.length; i++) {
          log.debug("  " + valves[i].getClass().getName());
        }
      }
      log.debug("======================");
    }

    // Make our application available if no problems were encountered
    if (ok) {
      context.setConfigured(true);
    } else {
      log.error(sm.getString("contextConfig.unavailable"));
      context.setConfigured(false);
    }
  }

  protected void webConfig() {
    WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                                                 context.getXmlValidation(), context.getXmlBlockExternal());
    // 加载全局的web.xml
    Set<WebXml> defaults = new HashSet<>();
    defaults.add(getDefaultWebXmlFragment(webXmlParser));

    WebXml webXml = createWebXml();

    // 开始解析webapp的web.xml
    InputSource contextWebXml = getContextWebXmlSource();
    if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
      ok = false;
    }

    ServletContext sContext = context.getServletContext();

    // Ordering is important here

    // Step 1. 加载所有jar包的web-fragment.xml
    Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

    // Step 2. 排序上面加载的web-fragment.xml.
    Set<WebXml> orderedFragments = null;
    orderedFragments =
      WebXml.orderWebFragments(webXml, fragments, sContext);

    // Step 3. 查找ServletContainerInitializer接口实现类
    if (ok) {
      processServletContainerInitializers();
    }

    if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
      // 处理web-fragment.xml
      processClasses(webXml, orderedFragments);
    }

    if (!webXml.isMetadataComplete()) {
      // Step 6. 合并所有的web-fragment.xml
      if (ok) {
        ok = webXml.merge(orderedFragments);
      }

      // Step 7. 合并默认配置
      webXml.merge(defaults);

      // Step 8. JSP转Servlet
      if (ok) {
        convertJsps(webXml);
      }

      // Step 9. 应用合并以后的web.xml对象
      if (ok) {
        configureContext(webXml);
      }
    } else {
      webXml.merge(defaults);
      convertJsps(webXml);
      configureContext(webXml);
    }

    if (context.getLogEffectiveWebXml()) {
      log.info("web.xml:\n" + webXml.toXml());
    }


    // Step 10. 查找静态资源
    if (ok) {
      // Spec does not define an order.
      // Use ordered JARs followed by remaining JARs
      Set<WebXml> resourceJars = new LinkedHashSet<>();
      for (WebXml fragment : orderedFragments) {
        resourceJars.add(fragment);
      }
      for (WebXml fragment : fragments.values()) {
        if (!resourceJars.contains(fragment)) {
          resourceJars.add(fragment);
        }
      }
      processResourceJARs(resourceJars);
    }

    // Step 11. 应用ServletContainerInitializer配置
    if (ok) {
      for (Map.Entry<ServletContainerInitializer,
           Set<Class<?>>> entry :
           initializerClassMap.entrySet()) {
        if (entry.getValue().isEmpty()) {
          context.addServletContainerInitializer(
            entry.getKey(), null);
        } else {
          context.addServletContainerInitializer(
            entry.getKey(), entry.getValue());
        }
      }
    }
  }

}

2.2 StandardPipeline初始化

BasicAuthenticatorStandardContextValue 分别初始化,Pipeline 的内容打算后面请求的时候再看…

三. StandardManager启动

StandardManager 就负责了 Session 的事情:

public class StandardManager extends ManagerBase {
  @Override
  protected synchronized void startInternal() throws LifecycleException {
        // 在父级抽象,准备Session的一些工具容器
    super.startInternal();

    // 读取被序列化到硬盘的session
    try {
      load();
    } catch (Throwable t) {
      ExceptionUtils.handleThrowable(t);
      log.error(sm.getString("standardManager.managerLoad"), t);
    }

    setState(LifecycleState.STARTING);
  }
}

到这里为止,所以异步启动容器的任务就都完成了,接下来就看看怎么处理请求。

四. 管理连接的类NioEndpoint

之前说过,在启动 StandardService 的时候,会初始化 connectors,这时候 Connector[HTTP/1.1-8080] 也会被初始化,这是一个处理 HTTP请求 的关键连接器。而这个连接器有一个 protocolHandler,也就是 协议处理器,现在 8.5 使用的是 Http11NioProtocol 这个处理类来处理。

Connector[HTTP/1.1-8080] 在初始化的时候,就会初始化 Http11NioProtocolHttp11NioProtocol 就包含了 Endpoint,理解为 终端口 吧,也就是请求进来的地方,在 NioEndpoint 初始化的时候,就会绑定到系统的端口里面:

public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {

  @Override
  public void bind() throws Exception {

    if (!getUseInheritedChannel()) {
      // 打开ServerSocketChannel
      serverSock = ServerSocketChannel.open();
      socketProperties.setProperties(serverSock.socket());
      InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
      // 绑定端口
      serverSock.socket().bind(addr,getAcceptCount());
    } else {
      // Retrieve the channel provided by the OS
      Channel ic = System.inheritedChannel();
      if (ic instanceof ServerSocketChannel) {
        serverSock = (ServerSocketChannel) ic;
      }
      if (serverSock == null) {
        throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
      }
    }
    serverSock.configureBlocking(true); //mimic APR behavior

    // 初始化acceptor、poller的线程数
    if (acceptorThreadCount == 0) {
      // FIXME: Doesn't seem to work that well with multiple accept threads
      acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
      //minimum one poller thread
      pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open();
  }


}

初始化完成以后,StandardService 会经过 startInternal 函数,即开启容器内部所有的组件,那么 NioEndpoint 相对应的组件 Acceptor Poller 即会被开启线程:

public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
  @Override
  public void startInternal() throws Exception {
    // 启动Connector的时候顺带启动NioEndpoint
    if (!running) {
      running = true;
      paused = false;

      processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                               socketProperties.getProcessorCache());
      eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                           socketProperties.getEventCache());
      nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                            socketProperties.getBufferPool());

      // 创建任务队列
      if ( getExecutor() == null ) {
        createExecutor();
      }

      // 初始化最大连接数,默认是10000
      initializeConnectionLatch();

      // 开启 poller 所有的线程
      pollers = new Poller[getPollerThreadCount()];
      for (int i=0; i<pollers.length; i++) {
        pollers[i] = new Poller();
        Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
        pollerThread.setPriority(threadPriority);
        pollerThread.setDaemon(true);
        pollerThread.start();
      }
      // 开启Acceptor所有的线程
      startAcceptorThreads();
    }
  }

  protected final void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new Acceptor[count];

    for (int i = 0; i < count; i++) {
      acceptors[i] = createAcceptor();
      String threadName = getName() + "-Acceptor-" + i;
      acceptors[i].setThreadName(threadName);
      Thread t = new Thread(acceptors[i], threadName);
      t.setPriority(getAcceptorThreadPriority());
      t.setDaemon(getDaemon());
      t.start();
    }
  }

  /**
   * 创建Worker线程,用来处理请求
   */
  public void createExecutor() {
    internalExecutor = true;
    TaskQueue taskqueue = new TaskQueue();
    TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
    executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
    taskqueue.setParent( (ThreadPoolExecutor) executor);
  }

  /**
   * 初始化连接限流器
   */
  protected LimitLatch initializeConnectionLatch() {
    if (maxConnections==-1) return null;
    if (connectionLimitLatch==null) {
      connectionLimitLatch = new LimitLatch(getMaxConnections());
    }
    return connectionLimitLatch;
  }

}

所以在这个开启中,启动了 acceptors pollers 以及 workers ,这些线程也是构成 Tomcat 处理请求返回响应的关键。