浅析springboot中tomcat源码实现

tomcat想必大家都非常的了解,在这里就不必介绍,在现在应用的springboot框架,tomcat就被内嵌其中。tomcat是怎么运行起来的?你知道怎么拓展tomcat吗?

tomcat启动流程


springboot加载流程

首先我们看一下springboot中tomcat的启动流程

1.找到AbstractApplicationContext方法中refresh方法,这个也是spring的启动流程,在refresh方法中,有一个onRefresh()方法,这个方法在spring中是一个空方法,在springboot对这个方法进行了拓展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

springboot拓展的onRefresh()方法如下

1
2
3
4
5
6
7
8
9
10
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}

很简单,看方法命名就知道这个创建了一个web容器,默认也就是tomcat容器。

在creatWebServer()方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();

//在这里创建了tomcat容器
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}

this.webServer = factory.getWebServer(getSelfInitializer());
在这里进行了详细的创建过程,打开这个方法的具体实现,如下就是具体的tomcat的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}

先看一下tomcat的整体架构

在tomcat中,最外层的就是tomcat,tomcat中有一个server。server中有多个service,
每个service可以单独对外服务。

service中有connector和container两个组件,关系如下

connector又分为endpoint和processor

endpoint处理传输层协议,演示版本为nio实现

processor处理应用层协议,演示版本为http1.1实现

中间通过适配器模式把request转换成servletRequest

全tomcat围绕着lifecycle接口进行组件生命周期的维护

在lifecycle接口中又有init,start等方法,tomcat初始化和启动就会围绕着这些方法进行实现

tomcat流程图就以深入理解java web技术内幕中servlet启动为例

(ps:需要注意的是网上有很多图是错误的,错误图中标明host和context的init方法在engine init之后启动)

下面我们详细的了解下tomcat中源码执行的过程

TomcatWebServer.initialize()方法中

1
this.tomcat.start();

正式开启tomcat的启动流程

tomcat启动流程


init过程

每一个容器都有自己的标准实现
在tomcat中start方法开启server.start();


server.start()方法实现为LifecycleBase.start()方法
调用init方法

init方法调用了initInternal()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}

try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal()
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}

initInternal方法为抽象方法,实现为子类的实现,谁调用谁实现,刚才为
StandardServer调用的,所有也由它实现

1
protected abstract void initInternal() throws LifecycleException;

在这里又用到了一个设计模式

模版方法模式

把通用的抽取到父类,子类只关注自己不同的实现即可

在后面的容器中全部运用了这种模版方法模式
包括init start stop方法等,后面就不在详细描述

在server标准实现中,StandardServer的initInternal具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{

super.initInternal();

if (engine != null) {
//1. 对engine的初始化
engine.init();
}

// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
//2. 对executor的初始化
executor.init();
}

// Initialize mapper listener
mapperListener.init();

// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
//3. 对connector的初始化
connector.init();
}
}
}

在service初始化的过程中,主要有三步

  • engine的初始化
1
engine.init();
  • executor的初始化
1
executor.init();
  • connector的初始化
1
2
3
4
for (Connector connector : connectors) {
//3. 对connector的初始化
connector.init();
}

engine的初始化

在engine初始化中,没有做什么特别的事,最主要的是并没有对后面host的初始化,对网上engine会对host进行初始化的纠正

executor的初始化也只是把自己这个组件加载进监控中,本身并没有做任何任何重要的事

重点看connector的初始化过程

在connect初始化中,有一个组件叫做protocolHandler:protocolHandler是Coyote协议的接口,通过endpoint和processor实现了对具体协议的处理能力。

在第二框中加载了具体的CoyoteAdapter实例

1
2
3
4
5
6
7
8
9
10
11
12
13
super.initInternal();

if (protocolHandler == null) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
}

// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
if (service != null) {
protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
}

然后进行protocolHandler的初始化

1
protocolHandler.init();

protocolHandler继续对endpoint进行初始化

1
endpoint.init();

前面我们说到endpoint是对传输层协议的实现,进去看是否有这样的实现

bindWithCleanup();方法,里面是bind()方法,具体的实现需要由子类实现,在这里是nio实现

bind里面也就是对socket的初始化和打开,具体可自己观看相应实现

start过程

在上面介绍了init的过程,下面接着介绍start过程

start一样,也是通过模版方法模式,主要看每个组件的startInternal方法实现

1
StandardServer.start()

循环调用services[i].start()方法

service标准实现StandardService
也是和init方法一样,对engine、exector、connector的start方法进行调用

在engine start方法中,还对host、context、wapper组件进行了初始化

exector start方法中对线程池进行了初步的设置,创建了自己的线程工厂,在后续任务中使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void startInternal() throws LifecycleException {

taskqueue = new TaskQueue(maxQueueSize);
TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
executor.setThreadRenewalDelay(threadRenewalDelay);
if (prestartminSpareThreads) {
executor.prestartAllCoreThreads();
}
taskqueue.setParent(executor);

setState(LifecycleState.STARTING);
}

Connector.start()调用流程如下

protocolHandler.start()->endpoint.start()->startInternal()交给具体的实现,这里还是nioEndpoint

我们进NioEndpoint start看一下关键点实现

NioEndpoint start方法中,对执行的线程池进行了创建

1
2
3
if (getExecutor() == null) {
createExecutor();
}
1
2
3
4
5
6
7
8
9
10
public void createExecutor() {
internalExecutor = true;
//创建任务队列
TaskQueue taskqueue = new TaskQueue();
//创建线程工厂,并设置成守护线程
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
//创建线程池
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}

我们看一下里面的具体参数,这也是tomcat对业务线程的默认参数

1
getMinSpareThreads()

里面的实现就是取最大线程和最小空闲线程的最小值,最大线程默认200,最小空闲线程默认10,取得值为10,所以tomcat核心线程默认为10

1
getMaxThreads()

最大线程默认为200

1
initializeConnectionLatch();

设置tomcat最大链接10000

1
startAcceptorThread();

监听socket套接字,由于nio的多路复用机制,所以可以用极少的线程监听大量的socket

1
serverSock.accept();

至此start核心流程基本结束

servlet运行流程

在socket监听到客户端信息之后,交由线程池处理
执行doRun方法

再到processor获取Coyote执行service方法

在coyote中转换request和response参数为servlet的ServletRequest和ServletResponse

1
postParseSuccess = postParseRequest(req, request, res, response);

在postParseRequest方法中对路径进行了查找,找到对应的servlet,里是通过mapper组件完成的

1
2
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());

把转换成功之后的request和response设置到管道中进行执行

1
2
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);

在管道中也运用了责任链模式,这和filter中是一样的,具体的大家可以单独研究这一块源码
每一个pipeline中都有一个大量的Valve,但是只有一个基础Valve
基础的Valve一般以容器+StandardValve命名,比如StandardengineValve
基础Valve调用下一层的容器阀。如上图所示

在CoyoteAdapter中开始调用enginePipeline开始

在engine中又开始吊桶host的第一个Valve

1
host.getPipeline().getFirst().invoke(request, response);

接下来的都是如此

filter和servlet执行流程

在StandardWrapperValve中开始构建filter链和servlet

首先在StandardWrapperValve invoke方法中

1
servlet = wrapper.allocate();

通过allocate反射获取servlet

再到

1
2
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

通过工厂获取filter链

把servlet设置进filterchain

在filterchain设置filterConfig

1
filterChain.addFilter(filterConfig);

循环添加filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void addFilter(ApplicationFilterConfig filterConfig) {

// Prevent the same filter being added multiple times
for(ApplicationFilterConfig filter:filters)
if(filter==filterConfig)
return;

if (n == filters.length) {
ApplicationFilterConfig[] newFilters =
new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;

}

然后执行internalDoFilter方法

1
internalDoFilter(request,response);

这个时候就真正的开始执行filter和servlet了

执行filter和servlet的时候,会循环执行filter,直到最后执行servlet

执行流程大体如此,每次执行filter pos次数+1,执行全部执行,然后开始执行servlet

整个tomcat流程执行完毕

tomcat涉及到的设计模式

当然tomcat细节远远不止如此,详细讲解可以出一本书,有兴趣的可以去了解下,tomcat中包含了特别多的设计模式
比如

  • 观察者模式,tomcat中对组件的监听都是基于观察者模式进行的
  • 模版方法模式:基本上全篇都是模版方法模式,在业务上我们也是常用这个模式
  • 适配器模式:在request转换到servletRequest的过程中,就使用了CoyoteAdapter进行了适配
  • 责任链模式:在tomcat中,pipeline和Valve组合,filter都是责任链模式的具体实现