深入源码了解 Tomcat 的构造

Tomcat 大家一直都在用,也用了好多年了,但是 Tomcat 究竟是啥,内部是咋样的,不知道~ 来,我从源码角度,给大家揭开它的面纱~

1. Tomcat架构

tomcat架构图.png

这个是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()方法,如图:

onRefresh.png

点开 createWebServer()方法:

createWebServer.png

进入TomcatServletWebServerFactory#getWebServer():

getWebServer.png

一步步来看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中。中,看图说话~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。