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