深入源码了解 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中。中,看图说话~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351