浅析springboot中tomcat源码实现

1,321 阅读8分钟

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();

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

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都是责任链模式的具体实现