tomcat想必大家都非常的了解,在这里就不必介绍,在现在应用的springboot框架,tomcat就被内嵌其中。tomcat是怎么运行起来的?你知道怎么拓展tomcat吗?
tomcat启动流程
springboot加载流程
首先我们看一下springboot中tomcat的启动流程
1.找到AbstractApplicationContext方法中refresh方法,这个也是spring的启动流程,在refresh方法中,有一个onRefresh()方法,这个方法在spring中是一个空方法,在springboot对这个方法进行了拓展。
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()方法如下
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
很简单,看方法命名就知道这个创建了一个web容器,默认也就是tomcat容器。
在creatWebServer()方法中
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的实现
@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()方法中
this.tomcat.start();
正式开启tomcat的启动流程
tomcat启动流程
init过程
每一个容器都有自己的标准实现 在tomcat中start方法开启server.start();
init方法调用了initInternal()方法
@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调用的,所有也由它实现
protected abstract void initInternal() throws LifecycleException;
在这里又用到了一个设计模式
模版方法模式
把通用的抽取到父类,子类只关注自己不同的实现即可
在后面的容器中全部运用了这种模版方法模式 包括init start stop方法等,后面就不在详细描述
在server标准实现中,StandardServer的initInternal具体实现
{
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的初始化
engine.init();
- executor的初始化
executor.init();
- connector的初始化
for (Connector connector : connectors) {
//3. 对connector的初始化
connector.init();
}
engine的初始化
在engine初始化中,没有做什么特别的事,最主要的是并没有对后面host的初始化,对网上engine会对host进行初始化的纠正
executor的初始化也只是把自己这个组件加载进监控中,本身并没有做任何任何重要的事
重点看connector的初始化过程
在connect初始化中,有一个组件叫做protocolHandler:protocolHandler是Coyote协议的接口,通过endpoint和processor实现了对具体协议的处理能力。
在第二框中加载了具体的CoyoteAdapter实例
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的初始化
protocolHandler.init();
protocolHandler继续对endpoint进行初始化
endpoint.init();
前面我们说到endpoint是对传输层协议的实现,进去看是否有这样的实现
bindWithCleanup();方法,里面是bind()方法,具体的实现需要由子类实现,在这里是nio实现
bind里面也就是对socket的初始化和打开,具体可自己观看相应实现
start过程
在上面介绍了init的过程,下面接着介绍start过程
start一样,也是通过模版方法模式,主要看每个组件的startInternal方法实现
StandardServer.start()
循环调用services[i].start()方法
service标准实现StandardService 也是和init方法一样,对engine、exector、connector的start方法进行调用
在engine start方法中,还对host、context、wapper组件进行了初始化
exector start方法中对线程池进行了初步的设置,创建了自己的线程工厂,在后续任务中使用
@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方法中,对执行的线程池进行了创建
if (getExecutor() == null) {
createExecutor();
}
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对业务线程的默认参数
getMinSpareThreads()
里面的实现就是取最大线程和最小空闲线程的最小值,最大线程默认200,最小空闲线程默认10,取得值为10,所以tomcat核心线程默认为10
getMaxThreads()
最大线程默认为200
initializeConnectionLatch();
设置tomcat最大链接10000
startAcceptorThread();
监听socket套接字,由于nio的多路复用机制,所以可以用极少的线程监听大量的socket
serverSock.accept();
至此start核心流程基本结束
servlet运行流程
在socket监听到客户端信息之后,交由线程池处理 执行doRun方法
再到processor获取Coyote执行service方法
在coyote中转换request和response参数为servlet的ServletRequest和ServletResponse
postParseSuccess = postParseRequest(req, request, res, response);
在postParseRequest方法中对路径进行了查找,找到对应的servlet,里是通过mapper组件完成的
connector.getService().getMapper().map(serverName, decodedURI,
version, request.getMappingData());
把转换成功之后的request和response设置到管道中进行执行
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
在管道中也运用了责任链模式,这和filter中是一样的,具体的大家可以单独研究这一块源码 每一个pipeline中都有一个大量的Valve,但是只有一个基础Valve 基础的Valve一般以容器+StandardValve命名,比如StandardengineValve 基础Valve调用下一层的容器阀。如上图所示
在CoyoteAdapter中开始调用enginePipeline开始
在engine中又开始吊桶host的第一个Valve
host.getPipeline().getFirst().invoke(request, response);
接下来的都是如此
filter和servlet执行流程
在StandardWrapperValve中开始构建filter链和servlet
首先在StandardWrapperValve invoke方法中
servlet = wrapper.allocate();
通过allocate反射获取servlet
再到
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
通过工厂获取filter链
把servlet设置进filterchain
在filterchain设置filterConfig
filterChain.addFilter(filterConfig);
循环添加filter
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方法
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都是责任链模式的具体实现