共计 3563 个字符,预计需要花费 9 分钟才能阅读完成。
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 中。中,看图谈话~