乐趣区

关于tomcat:深入源码了解-Tomcat-的构造

Tomcat 外部原理

Tomcat 大家始终都在用,也用了好多年了,然而 Tomcat 到底是啥,外部是咋样的,不晓得~ 来,我从源码角度,给大家揭开它的面纱~

1. Tomcat 架构

这个是 tomcat 的架构图,专治密集恐惧症患者~~ 虚线的代表同一多个

  • Server:代表一个运行的 tomcat 实例,蕴含一个或多个 service 子容器
  • Service: 代表 tomcat 中一组解决申请,提供服务的组件,蕴含多个 Connector 和一个 Container
  • Connector: 在指定 IP、端口、协定下监听客户端申请,创立 Request、Response 给 Engine,从 Engine 取得响应给客户端。Connector 是通过 ProtocolHandler 来解决申请的,ProtocolHandler 蕴含三个部件:Endpoint、Processor、Adapter

    • Endpoint: 解决底层 socket 网络连接。Acceptor 监听申请,Handler 解决接管到的 socket, AsyncTimeout 查看异步 Request 的超时
    • Processor: 将 Endpoint 接管到的 Socket 封装成 Request
    • Adapter: 将 Request 交给 Container 解决
  • Container: 容器的父接口,用于封装和治理 Servlet,采纳责任链设计模式,蕴含四个组件

    • Engine: 可运行的 servlet 引擎实例,一个服务中只有一个。次要性能是将申请委托给适当虚拟主机解决。
    • Host: Engine 的子容器,一个 Host 代表一个虚拟主机,一个虚拟主机能够部署一个或多个 Web App,每个 Web App 对应一个 Context
    • Context: 代表 Servlet 的 Context,是 Servlet 的根本运行环境,示意 web 利用自身。它最重要性能是治理外面的 servlet
    • Wrapper: 示意一个独自的 Servlet,是 Context 的子容器,也是最底层的容器,没有子容器了。治理 Servlet 的装载、初始化、执行和资源回收

2. Tomcat 源码

咱们来追踪 SpringBoot 启动过程,看一下它是怎么创立 Tomcat 的。

跟到 ServletWebServerApplicationContext#refresh() 办法,如图:

点开 createWebServer() 办法:

进入 TomcatServletWebServerFactory#getWebServer():

一步步来看 tomcat 的构建过程。

  • 设置运行门路

    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
  • 增加 Connector

    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);

    先点 getService() 进去看下:

    public Service getService() {return getServer().findServices()[0];
    }

    再点 getServer() 看看:

    public Server getServer() {if (server != null) {return server;}
          System.setProperty("catalina.useNaming", "false");
          server = new StandardServer();
          initBaseDir();
          ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
          server.setPort(-1);
          // server 里增加一个名为”tomcat“的 service
          Service service = new StandardService();
          service.setName("Tomcat");
          server.addService(service);
          return server;
    }

    tomcat 里,先创立 server,server 设置关联的 service,service 再增加 connector,跟咱们下面的架构图截然不同。

    再来看 Host:

    tomcat.getHost().setAutoDeploy(false);

    点击 getHost() 进去:

    public Host getHost() {Engine engine = getEngine();
        if (engine.findChildren().length > 0) {return (Host) engine.findChildren()[0];
         }
         Host host = new StandardHost();
         host.setName(hostname);
         // Engine 增加 host
         getEngine().addChild(host);
         return host;
    }

    能够看到,Host 是被当做子容器增加到 Engine 里的,比照架构图,没骗你吧~~

    再点 getEngine() 进去:

    public Engine getEngine() {Service service = getServer().findServices()[0];
        if (service.getContainer() != null) {return service.getContainer();
         }
         Engine engine = new StandardEngine();
         engine.setName("Tomcat");
         engine.setDefaultHost(hostname);
         // engine 设置为 service 的容器
         service.setContainer(engine);
         return engine;
    }

    又能够看到,engine 被当做 service 的容器设置进去了,没有问题。

    回到 getWebServer(),看这一行:

    configureEngine(tomcat.getEngine());

    点击这个办法进去:

    private void configureEngine(Engine engine) {engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);
        for (Valve valve : this.engineValves) {engine.getPipeline().addValve(valve);
        }
    }

    engine 里有 pipeline,pipeline 一个个增加 valve,串成链。

    如同还漏了架构图的两个货色,context 和 servlet,回到 getWebServer() 持续点击:

    prepareContext(tomcat.getHost(), initializers);

    点击该办法进去,看要害代码:

    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {File documentRoot = getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
            context.setName(getContextPath());
            ……
            if (isRegisterDefaultServlet()) {addDefaultServlet(context);
            }
            if (shouldRegisterJspServlet()) {addJspServlet(context);
                addJasperInitializer(context);
            }
            context.addLifecycleListener(new StaticResourceConfigurer(context));
            ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
            host.addChild(context);
            configureContext(context, initializersToUse);
            postProcessContext(context);
        }

    这外面都是设置 context 的,包含增加默认的 servlet,最初 context 以子容器的模式增加到了 host 中。中,看图谈话~

退出移动版