目录
2.1AbstractApplicationContext类的refresh()方法启动Tomcat
2.2EmbeddedWebApplicationContext类CreateEmbeddedServletContainer方法
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。
组件及一次请求的图
1.1Tomcat的主要组件
1.1.1.Server:
是整个Tomcat的容器,里面包含tomcat的所有组件,可以包含多个Service。
1.1.2.Sevice:
是包含Connector和Container的集合。
Service用合适的Connector接收用户的请求,再发给相应的Container来处理。
一个 Service 可以设置多个 Connector,但是只能有一个 Container 容器。
1.1.3.Connector:
负责的是底层的网络通信的实现。监听端口。
支持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
方法调用顺序图
1.1.4.Container:
负责的是上层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.Component
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了。
1.2Lifecycle生命周期
控制组件声明周期,由观察者模式实现。
1.2.1类图
1.2.2 状态图
1.2.3接口Lifecycle
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();
1.2.4 LifecycleBase
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;
二、Spring Boot启动内置Tomcat源码解读
环境SpringBoot 1.5.8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
SpringBoot在启动的时候会去启动Tomcat。在如下方法中:
2.1AbstractApplicationContext类的refresh()方法启动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));
}
}
2.2EmbeddedWebApplicationContext类CreateEmbeddedServletContainer方法
创建及初始化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());
2.2.1getSelfInitializer方法
先是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.2getEmbeddedServletContainer方法
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<Service, Connector[]> 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<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future<Void> 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
2.3EmbeddedWebApplicationContext类的startEmbeddedServletContainer方法
启动内置的ServletContainer,这里即是启动Tomcat的Connector。
发布内置ServletContainer已初始化的事件。
private EmbeddedServletContainer startEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
if (localContainer != null) {
//启动内置的tomcat余下所有组件
localContainer.start();
}
return localContainer;
}
跟到localContainer.start()方法中可以看到是调用了TomcatEmbeddedServletContainer类的start()方法
2.3.1TomcatEmbeddedServletContainer类的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