目录
一、简介
1.1Tomcat的主要组件
1.1.1.Server:
1.1.2.Sevice:
1.1.3.Connector:
1.1.4.Container:
1.1.5.Component
1.2Lifecycle生命周期
1.2.1类图
1.2.2 状态图
1.2.3接口Lifecycle
1.2.4 LifecycleBase
二、Spring Boot启动内置Tomcat源码解读
2.1AbstractApplicationContext类的refresh()方法启动Tomcat
2.2EmbeddedWebApplicationContext类CreateEmbeddedServletContainer方法
2.2.1getSelfInitializer方法
2.2.2getEmbeddedServletContainer方法
2.3EmbeddedWebApplicationContext类的startEmbeddedServletContainer方法
2.3.1TomcatEmbeddedServletContainer类的start()方法
Tomcat 就是一个 Servlet 容器, 能接收用户从浏览器发来的请求, 然后转发给 Servlet 处理, 把处理完的响应数据发回浏览器。
在实际的应用中,Apache/Nginx 作为静态资源服务器和负载均衡服务器放在最前端, 后面是 tomcat 组成的集群。
如果用户请求的是静态资源, Nginx直接处理, 如果是动态资源(如xxx.jsp), Nginx就会按照一定的算法转发的某个 Tomcat上, 达到负载均衡的目的。
示例全是使用的spring boot 1.5.8的内置tomcat。为tomcat8.5。用的http 1.1 和nio。
组件及一次请求的图
是整个Tomcat的容器,里面包含tomcat的所有组件,可以包含多个Service。
是包含Connector和Container的集合。
Service用合适的Connector接收用户的请求,再发给相应的Container来处理。
一个 Service 可以设置多个 Connector,但是只能有一个 Container 容器。
负责的是底层的网络通信的实现。监听端口。
支持HTTP协议和AJP协议(用于tomcat和apache静态服务器通信)。
Connector接受请求,以命令的方式交给Container去处理请求,该过程由命令模式实现。
1.接受socket
2.从socket获取数据包,并解析成HttpServletRequest对象
3.从engine容器开始走调用流程,经过各个value,到FilterChain,最后调用Servlet完成业务逻辑
4.返回response,关闭socket
其中:
1.1.3.1Http11NioProtocol
默认使用的HTTP/1.1 ,对于tomcat8 以更高版HTTP/1.1协议会默认使用nio,即是org.apache.coyote.http11.Http11NioProtocol。
org.apache.coyote.http11.Http11Protocol | bio | 使用ServerSocket处理请求 | JIoEndpoint |
org.apache.coyote.http11.Http11NioProtocol | nio | 使用SocketChannel处理请求 | NioEndpoint |
org.apache.coyote.http11.Http11Nio2Protocol | nio2 | 使用AsynchronousSocketChannel处理请求 | Nio2Endpoint |
org.apache.coyote.http11.Http11AprProtocol | apr | AprEndPoint |
Connector在处理HTTP请求时,不同的Tomcat版本支持的protocol不同,其中最典型的protocol包括BIO、NIO和APR(Tomcat7中支持这3种,Tomcat8增加了对NIO2的支持,而到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持)。
如果没有指定protocol,则使用默认值HTTP/1.1,在Tomcat7中,自动选取使用BIO或APR,在Tomcat8中,自动选取使用NIO或APR(如果找到APR需要的本地库,则使用APR,否则使用NIO)。
1.1.3.2 NioEndpoint 中有。
Acceptor,初始启动1个线程,名字:http-nio-8083-Acceptor-0
Poller线程池,初始启动2个线程(单核 CPU 为 1,多核为 2),名字:http-nio-8083-ClientPoller-0
Executor线程池,初始启动核心线程数10个,最大线程数200,名字:http-nio-8083-exec-1
1.1.3.2.1 Acceptor 接受socket请求,将一个Java标准SocketChannel套接字请求通道封装成一个Tomcat的NioChannel,从Poller线程池中拿一个线程出来,进入Poller的events队列,把这个请求委托个Poller线程去处理。Acceptor--------Poller是一个生产者-消费者模式。
方法调用顺序图
1.1.3.2.2 Poller 实现Runnable接口,run()方法中会轮询去消费events队列,拿出请求,然后委托的SocketProcessor和Executor的线程池去处理。
每个 poller 关联了一个 Selector。
每个poller维护了一个event同步队列。
方法调用顺序图
1.1.3.2.3 Executor 线程会启动NioEndPoint中SocketProcessor的doRun方法,该方法又会去调AbstractProtocol.ConnectionHandler的process方法。
1.1.3.3 AbstractProtocol.ConnectionHandler 对象维护了一个Http11Processor对象, 这个类中就把SocketProcessor和Http11Processor 关联起来。Http11Processor的service方法会去调CoyoteAdapter。
1.1.3.4 CoyoteAdapter 将Connector组件和Engine容器连接起来,把Connector处理得到的Request和Response对象传到Engine容器,调用它的管道。
1.1.3.5 Mapper 客户端请求的路由导航组件,可以通过请求地址找到对应的Servlet
方法调用顺序图
负责的是上层servlet业务的实现。
Container由责任链模式实现。
处理请求的容器,把处理请求的处理器包装为Valve(阀门)对象,并按一定顺序放入类型为Pipeline(管道)的管道里。
container的四个子容器组件:
1.1.4.1.Engine
一个 Engine 代表一个完整的 Servlet 引擎。
1.1.4.2.Host
一个 Host 在 Engine 中代表一个虚拟主机。
例如请求地址中的红色部分http://www.sid.com/post/index.html
1.1.4.3.Context:
就是我们所部属的具体Web应用的Context,每个请求都在是相应的Context里处理的。
例如请求地址中的红色部分http://www.sid.com/post/index.html
1.1.4.4.Wrapper:
每个Servlet都有相应的Wrapper来管理。Wrapper实际上是Tomcat容器内部对于 Servlet 的封装。
例如请求地址中的红色部分http://www.sid.com/post/index.html
wrapper主要包括三大类
1.处理静态资源的一个Wrapper:例如html,jpg.对应wrapper为org.apache.catalina.servlets.DefaultServlet
2.处理jsp文件,对应wrapper为org.apache.jasper.servlet.JspServlet
3.自定义servlet对象,在web.xml中定义的servlet
wrapper的pipeline中走到最后一个value会去调FitlerChain, FitlerChain走完了就调Servlet。
1.1.5.1Manager管理器
被Container用来管理Session池
1.1.5.2logger日志管理
日志
1.1.5.3loader载入器
被Container用来载入各种所需的Class
1.1.5.4pipeline管道
使用责任链模式。要处理请求的话,请求会被放进容器的管道(pipeline)里面去。 管道中的各个Value(阀门)会“拦截”下管道中的需求做相应的处理。pipeline类似于FilterChain,每一个Value类似于一个Filter类。
每一层容器有自己的pipeline,不是所有容器共用一个pipeline。
1.1.5.5value管道中的阀门
每个Pipeline 都有特定的BaseValue ,而且是在Pipeline 管道的最后一个执行。
在上层容器的Pipeline 管道的最后一个特定BaseValue 中会调用下层容器的Pipeline 管道。
4个容器对应的特定BaseValue类名为:
StandardEngine---->StandardEngineValue
StandardHost---->StandardHostValue
StandardContext---->StandardContextValue
StandardWrapper---->StandardWrapperValue
最后在StandardWrapperValue中会生成FilterChain,调filter
这里生成的Filter类有5个
name=characterEncodingFilter, filterClass=org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
name=hiddenHttpMethodFilter, filterClass=org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
name=httpPutFormContentFilter, filterClass=org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
name=requestContextFilter, filterClass=org.springframework.boot.web.filter.OrderedRequestContextFilter
name=Tomcat WebSocket (JSR356) Filter, filterClass=org.apache.tomcat.websocket.server.WsFilter
filter调完了会调Servlet
调用的org.springframework.web.servlet.FrameworkServlet类的service方法,它的默认实现是DispatcherServlet。
这就进入spring了。
控制组件声明周期,由观察者模式实现。
Tomcat通过org.apache.catalina.Lifecycle 接口来统一管理生命周期,所有有生命周期的组件都要实现 Lifecycle 接口。
该接口定义了13个 String 类型常量,用于 LifecycleEvent 时间的 type 属性中,作用是区分组件发出的 LifecycleEvent 事件时的状态。
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
定义了3个监听器方法,用来添加、查找和删除 LifecycleListener 类型的监听器。
//添加监听器
public void addLifecycleListener(LifecycleListener listener);
//获取所有监听器
public LifecycleListener[] findLifecycleListeners();
//移除某个监听器
public void removeLifecycleListener(LifecycleListener listener);
定义了4个生命周期的方法:
//初始化
public void init() throws LifecycleException;
//开始
public void start() throws LifecycleException;
//结束
public void stop() throws LifecycleException;
//摧毁
public void destroy() throws LifecycleException;
定义获取当前状态的方法 getState 和 getStateName,用来获取当前的状态。
//获取声明周期状态
public LifecycleState getState();
//获取状态名字(字符串类型的声明周期状态)
public String getStateName();
Lifecycle接口的默认实现类是 org.apache.catalina.LifecycleBase,所有有生命周期的组件都直接或间接的继承自 LifecycleBase。
LifecycleBase对Lifecycle接口的方法有基本的实现
但LifecycleBase还有几个未实现的模板方法,将由其子类实现
protected abstract void destroyInternal() throws LifecycleException;
protected abstract void initInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
环境SpringBoot 1.5.8
org.springframework.boot
spring-boot-starter-web
SpringBoot在启动的时候会去启动Tomcat。在如下方法中:
其中
2.1.1 onRefresh()方法中会调用EmbeddedWebApplicationContext类的CreateEmbeddedServletContainer方法,
创建及初始化Tomcat类,并且启动,启动了Tomcat的Server、Service、Container(Engine、Host、Context、Wrapper、Realm、Pipeline、Value)、MapperListerner。
2.1.2 finishRefresh()方法会调用EmbeddedWebApplicationContext类的startEmbeddedServletContainer方法,
启动内置的ServletContainer,这里即是启动Tomcat的Connector。
发布内置ServletContainer已初始化的事件。
protected void finishRefresh() {
//调用父类的finishRefresh方法
super.finishRefresh();
//启动内置的ServletContainer,这里即是启动Tomcat余下的所有组件。
EmbeddedServletContainer localContainer = startEmbeddedServletContainer();
if (localContainer != null) {
//发布内置ServletContainer已初始化的事件。
publishEvent(
new EmbeddedServletContainerInitializedEvent(this, localContainer));
}
}
创建及初始化Tomcat类,并且启动,启动了Tomcat的Server、Service、Container(Engine、Host、Context、Wrapper、Realm、Pipeline、Value)、MapperListerner。
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
//第一次启动这两个都为空
if (localContainer == null && localServletContext == null) {
//得到Servlet容器工厂
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//通过Servlet容器工厂创建Servlet容器
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
//初始化属性源
initPropertySources();
}
重点在这句话。
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
先是getSlfInitializer返回一个ServletContextInitializer初始化器,用来初始化Tomcat。这个new出来的ServletContextInitializer实例的onStartup方法会在之后tomcat StandardContext启动中被调用
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
selfInitialize(servletContext);
}
};
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareEmbeddedWebApplicationContext(servletContext);
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
beanFactory);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
getServletContext());
existingScopes.restore();
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
getServletContext());
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
最后的for循环做的事情对应console中打印的日志:
映射了dispatcherServlet、characterEncodingFilter、hiddenHttpMethodFilter、httpPutFormContentFilter、requestContextFilter的URL
2.2.2.1getSlfInitializer方法
产生的初始化器会作为getEmbeddedServletContainer方法的入参。
这个方法前面的都是生成各种组件,配置各种组件,最后的getTomcatEmbeddedServletContainer(tomcat)创建tomcat。
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
//生成tomcat类
Tomcat tomcat = new Tomcat();
//创建临时目录
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
//设置基础路径
tomcat.setBaseDir(baseDir.getAbsolutePath());
//创建Connector 协议用的 Http11NioProtocol
Connector connector = new Connector(this.protocol);
//创建StandardServer,初始化基础路径。
//创建StandardService,设置名字
//把service注册到server中
//把Connector注册到service中
tomcat.getService().addConnector(connector);
//设置connector的一些属性,端口,协议地址,URI编码,SSL,压缩
customizeConnector(connector);
//设置connector
tomcat.setConnector(connector);
//创建并且配置Engine,注册到service中。
//创建StandradHost,把engine.addChild(host)
//host关闭自动部署
tomcat.getHost().setAutoDeploy(false);
//配置engine,设置后台线程延迟时间
configureEngine(tomcat.getEngine());
//第一次启动size=0 后面再来看它做了啥
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//准备context,这里的initializers就是前面外层getSlfInitializer方法生成传进来的
//创建Context,这里用的TomcatEmbeddedContext,设置属性(名字,显示名字,路径,doc路径,生命周期监听器,类加载器)
//创建StandardWrapper,配置属性,servlet设置成默认的org.apache.catalina.servlets.DefaultServlet
//把servlet/Wrapper注册到context中
//把servletmapping注册到context中
//给context添加声明周期监听器
//合并初始化器
//配置context,设置starter,添加初始化器,配置session
//把context加入到host中
prepareContext(tomcat.getHost(), initializers);
//创建tomcat内置servlet容器并且初始化,启动tomcat
return getTomcatEmbeddedServletContainer(tomcat);
}
跟到2.2.2.2 getTomcatEmbeddedServletContainer(tomcat)方法中
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
}
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
在创建该类的时候会执行一个2.2.2.3 initialize方法,该方法中正儿八经开始启动tomcat
private void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
//全局原子变量containerCounter+1,由于初始值是-1
addInstanceIdToEngineName();
try {
//将上面new的connection以service(这里是StandardService[Tomcat])做key保存到private final Map serviceConnectors中
//并将StandardService中的protected Connector[] connectors与service解绑
//解绑后下面利用LifecycleBase启动容器就不会启动到Connector了。
removeServiceConnectors();
//启动tomcat服务,触发监听器
this.tomcat.start();
// 检查初始化过程中的异常,如果有直接在主线程抛出
rethrowDeferredStartupExceptions();
Context context = findContext();
try {
//绑定context和classloader
ContextBindings.bindClassLoader(context, getNamingToken(context),
getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Tomcat所有的线程都是守护线程
//所以创建一个非守护线程来避免服务到这就shutdown了
startDaemonAwaitThread();
}
catch (Exception ex) {
containerCounter.decrementAndGet();
throw ex;
}
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat", ex);
}
}
}
重点看一下方法
this.tomcat.start();
跟进去到Tomcat类
public void start() throws LifecycleException {
getServer();
getConnector();
server.start();
}
这里可以看到2.2.2.4 server.start();
调用该方法后会依次调用:
StandardServer类的startInternal()
StandardService类的startInternal()
StandardEngine类的startInternal()
ContainerBase类的startInternal()
RealmBase类的startInternal()
StandardPipeline类的startInternal()
ValueBase类的startInternal()
MapperListener类的startInternal()
即在这里启动Server的时候就把Service、Container(Engine 、Realm、Pipeline、Value)、 Mapperlistener一起启动了。
给Container添加监听器(ContainerListener、LifecycleListener)
2.2.2.4 ContainerBase类的startInternal方法
这里要注意ContainerBase类的startInternal方法。
StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程中。
StandardEngine[Tomcat].StandardHost[localhost]的启动在ContainerBase类的startInternal方法中另外起了一个线程来启动
protected synchronized void startInternal() throws LifecycleException {
//省略...
List> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
if (fail) {
throw new LifecycleException(
sm.getString("containerBase.threadedStartFailed"));
}
//省略...
}
这句话就是另外启动一个线程来启动 StandardEngine[Tomcat].StandardHost[localhost]
这里该线程的名字叫Tomcat-startStop-1,5,main
results.add(startStopExecutor.submit(new StartChild(children[i])));
这个线程会启动StandardHost、没有ErrorReportValue则在PipeLine中添加
再启动一个线程localhost-startStop-1,5,main,该线程启动StandardContext、启动StandardWrapper
启动内置的ServletContainer,这里即是启动Tomcat的Connector。
发布内置ServletContainer已初始化的事件。
private EmbeddedServletContainer startEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
if (localContainer != null) {
//启动内置的tomcat余下所有组件
localContainer.start();
}
return localContainer;
}
跟到localContainer.start()方法中可以看到是调用了TomcatEmbeddedServletContainer类的start()方法
public void start() throws EmbeddedServletContainerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
//启动Service对应的每个Connector
//创建CoyoteAdapter
//初始化Http11protocol
//初始化NioEndPoint
//启动Connector
//启动Http11protocol
//启动NioEndPoint,绑定监听端口启动,启动selector线程(1个),创建Executor线程池(核心线程数10,最大线程数200),初始化最大连接数connectionLatch默认10000,开始poller线程池(2个线程)、开始Acceptor线程(1个)
addPreviouslyRemovedConnectors();
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
startConnector(connector);
}
//检查connector是否已经启动
checkThatConnectorsHaveStarted();
this.started = true;
TomcatEmbeddedServletContainer.logger
.info("Tomcat started on port(s): " + getPortsDescription(true));
}
catch (ConnectorStartFailedException ex) {
stopSilently();
throw ex;
}
catch (Exception ex) {
throw new EmbeddedServletContainerException(
"Unable to start embedded Tomcat servlet container", ex);
}
finally {
Context context = findContext();
ContextBindings.unbindClassLoader(context, getNamingToken(context),
getClass().getClassLoader());
}
}
}
主要是启动Tomcat的Connector