深入源码了解 Tomcat 的构造

Tomcat内部原理

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

1. Tomcat架构

深入源码了解 Tomcat 的构造_第1张图片

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

深入源码了解 Tomcat 的构造_第2张图片

点开 createWebServer()方法:

深入源码了解 Tomcat 的构造_第3张图片

进入TomcatServletWebServerFactory#getWebServer():

深入源码了解 Tomcat 的构造_第4张图片

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

你可能感兴趣的:(tomcat源码)