springboot内置tomcat启动全流程详解及处理请求详解

Posted OkidoGreen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot内置tomcat启动全流程详解及处理请求详解相关的知识,希望对你有一定的参考价值。

(1518条消息) springboot内置tomcat之NIO处理流程详解_CRUD的W的博客-CSDN博客_springboot tomcat niohttps://blog.csdn.net/qq_31086797/article/details/107703053

前言

springboot默认内置了tomcat容器,启动时可以直接启动内置tomcat来启动应用

本文要介绍的内容有

  • tomcat启动的全流程
  • 发起http请求后tomcat转发请求到我们的DispatcherServlet的全过程

tomcat启动的全流程

直接从源码开始分析,先从run方式开始分析,我们只关注主要流程,只关心核心代码

public ConfigurableApplicationContext run(String... args) 
		    ......
			// 核心代码,刷新spring上下文
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) 
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			
			listeners.started(context);
			callRunners(context, applicationArguments);
		
		catch (Throwable ex) 
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		

		try 
			listeners.running(context);
		
		catch (Throwable ex) 
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		
		return context;
	

核心方法 refreshContext(context);

private void refreshContext(ConfigurableApplicationContext context) 
		// 核心代码
		refresh(context);
		if (this.registerShutdownHook) 
			try 
				context.registerShutdownHook();
			
			catch (AccessControlException ex) 
				// Not allowed in some environments.
			
		
	
protected void refresh(ApplicationContext applicationContext) 
	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
	((AbstractApplicationContext) applicationContext).refresh();

继续看 refresh(),有三个实现类,默认的是servlet环境,所以看实现类ServletWebServerApplicationContext

@Override
	public final void refresh() throws BeansException, IllegalStateException 
		try 
			super.refresh();
		
		catch (RuntimeException ex) 
			stopAndReleaseWebServer();
			throw ex;
		
	

继续看 super.refresh(),父类AbstractApplicationContext的refresh()

@Override
	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 
				......

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

回到ServletWebServerApplicationContext的onRefresh()

@Override
	protected void onRefresh() 
		super.onRefresh();
		try 
			// 核心代码,创建web容器
			createWebServer();
		
		catch (Throwable ex) 
			throw new ApplicationContextException("Unable to start web server", ex);
		
	

这里就可以看到核心代码了createWebServer(),创建一个web容器

private void createWebServer() 
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) 
			ServletWebServerFactory factory = getWebServerFactory();
			// 核心代码
			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();
	

到了TomcatServletWebServerFactory的getWebServer

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) 
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory
				: createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		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);
	
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) 
	return new TomcatWebServer(tomcat, getPort() >= 0);

public TomcatWebServer(Tomcat tomcat, boolean autoStart) 
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		// 核心代码
		initialize();
	

进入initialize()

private void initialize() throws WebServerException 
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) 
			try 
				......

				// Start the server to trigger initialization listeners
				// 核心代码
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try 
					ContextBindings.bindClassLoader(context, context.getNamingToken(),
							getClass().getClassLoader());
				
				catch (NamingException ex) 
					// Naming is not enabled. Continue
				

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			
			catch (Exception ex) 
				stopSilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			
		
	

进入tomcat.start()

 @Override
    public final synchronized void start() throws LifecycleException 

        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) 

            if (log.isDebugEnabled()) 
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
             else if (log.isInfoEnabled()) 
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            

            return;
        

        if (state.equals(LifecycleState.NEW)) 
        	// 核心代码1
            init();
         else if (state.equals(LifecycleState.FAILED)) 
            stop();
         else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) 
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        

        try 
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            // 核心代码2
            startInternal();
           ......
         catch (Throwable t) 
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            handleSubClassException(t, "lifecycleBase.startFail", toString());
        
    

这里有两个核心代码init()和startInternal()
先看init方法

    @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这里实现类太多了,我们关注重点组件Connector,进入initInternal方法,来到了Connector类的initInternal()

@Override
    protected void initInternal() throws LifecycleException 

        ......

        try 
        	// 核心代码
            protocolHandler.init();
         catch (Exception e) 
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        
    

进入AbstractProtocol的init()

 @Override
    public void init() throws Exception 
        if (getLog().isInfoEnabled()) 
            getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        

        if (oname == null) 
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) 
                Registry.getRegistry(null, null).registerComponent(this, oname, null);
            
        

        if (this.domain != null) 
            rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent(
                    getHandler().getGlobal(), rgOname, null);
        

        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        endpoint.setDomain(domain);
		// 核心代码
        endpoint.init();
    

endpoint.init()

 public final void init() throws Exception 
        if (bindOnInit) 
        	// 核心代码
            bind();
            bindState = BindState.BOUND_ON_INIT;
        
        if (this.domain != null) 
            // Register endpoint (as ThreadPool - historical name)
            oname = new ObjectName(domain + ":type=ThreadPool,name=\\"" + getName() + "\\"");
            Registry.getRegistry(null, null).registerComponent(this, oname, null);

            for (SSLHostConfig sslHostConfig : findSslHostConfigs()) 
                registerJmx(sslHostConfig);
            
        
    

这个bind方法很关键,这里有多个实现类,当前我的版本默认环境是NIO,即进入NioEndpoint类

 @Override
    public void bind() throws Exception 
    	// 核心代码
        initServerSocket();

        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount == 0) 
            // FIXME: Doesn't seem to work that well with multiple accept threads
            acceptorThreadCount = 1;
        
        if (pollerThreadCount <= 0) 
            //minimum one poller thread
            pollerThreadCount = 1;
        
        setStopLatch(new CountDownLatch(pollerThreadCount));

        // Initialize SSL if needed
        initialiseSsl();

        selectorPool.open();
    

initServerSocket可以看出是初始化Socket的

protected void initServerSocket() throws Exception 
    if (!getUseInheritedChannel()) 
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getAcceptCount());
     else 
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) 
            serverSock = (ServerSocketChannel) ic;
        
        if (serverSock == null) 
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        
    
    serverSock.configureBlocking(true); //mimic APR behavior

可以看到我们熟悉的Socket代码了,绑定了ip端口,至此Socket初始化完成

然后我们看之前的核心代码2,startInternal()方法
同样我们看关键组件Connector的startInternal()

@Override
protected void startInternal() throws LifecycleException 

    // Validate settings before starting
    if (getPort() < 0) 
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
    

    setState(LifecycleState.STARTING);

    try 
    	// 核心代码
        protocolHandler.start();
     catch (Exception e) 
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    

protocolHandler.start()

 @Override
    public void start() throws Exception 
        if (getLog().isInfoEnabled()) 
            getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        
		// 核心代码
        endpoint.start();

        // Start async timeout thread
        asyncTimeout = new AsyncTimeout();
        Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
        int priority = endpoint.getThreadPriority();
        if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) 
            priority = Thread.NORM_PRIORITY;
        
        timeoutThread.setPriority(priority);
        timeoutThread.setDaemon(true);
        timeoutThread.start();
    

endpoint.start()

public final void start() throws Exception 
    if (bindState == BindState.UNBOUND) 
        bind();
        bindState = BindState.BOUND_ON_START;
    
    // 核心代码
    startInternal();

进入NioEndpoint的startInternal()

 public void startInternal() throws Exception 

        if (!running) 
            ......

            // Start poller threads
            // 核心代码
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) 
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            

            startAcceptorThreads();
        
    

这里的核心代码pollers数组很关键,创建了多个Poller类并且放到了线程里面启动了,从这里可以看出来Poller是一个实现了Runnable类的线程类
线程启动后就会执行Poller类的run方法了

 @Override
    public void run() 
        // Loop until destroy() is called
        
        // 核心代码1,轮询
        while (true) 

            boolean hasEvents = false;

            try 
                if (!close) 
                    hasEvents = events();
                    if (wakeupCounter.getAndSet(-1) > 0) 
                        //if we are here, means we have other stuff to do
                        //do a non blocking select
                        keyCount = selector.selectNow();
                     else 
                        keyCount = selector.select(selectorTimeout);
                    
                    wakeupCounter.set(0);
                
                if (close) 
                    events();
                    timeout(0, false);
                    try 
                        selector.close();
                     catch (IOException ioe) 
                        log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                    
                    break;
                
             catch (Throwable x) 
                ExceptionUtils.handleThrowable(x);
                log.error("",x);
                continue;
            
            //either we timed out or we woke up, process events first
            if ( keyCount == 0 ) hasEvents = (hasEvents | events());

            Iterator<SelectionKey> iterator =
                keyCount > 0 ? selector.selectedKeys().iterator() : null;
            // Walk through the collection of ready keys and dispatch
            // any active event.
            while (iterator != null && iterator.hasNext()) 
                SelectionKey sk = iterator.next();
                NiosocketWrapper attachment = (NioSocketWrapper)sk.attachment();
                // Attachment may be null if another thread has called
                // cancelledKey()
                if (attachment == null) 
                    iterator.remove();
                 else 
                    iterator.remove();
                    // 核心代码2
                    processKey(sk, attachment);
                
            //while

            //process timeouts
            timeout(keyCount,hasEvents);
        //while

        getStopLatch().countDown();
    

这里启动了一个while(true)循环,不停的监听NIO接受的请求,直到收到数据执行核心代码2,processKey(sk, attachment)

到这里为止,我们已经启动了多个监听线程对象Poller,Poller类会轮询监听socket连接,即我们的Tomcat容器已经启动完成了。

处理http请求

DispatcherServlet

DispatcherServlet是我们在学习spring mvc的一个非常重要的servlet,我们都知道http请求后会进入DispatcherServlet的doService方法,那么这是怎么进入的呢?接下来我们就来看一下源码

我们发起一个http请求访问一下我们的一个测试的controller方法,我们在上述的核心代码2,processKey(sk, attachment)处打上断点

发起请求后进入了我们的断点,processKey方法

 protected void processKey(SelectionKey sk, NioSocketWrapper attachment) 
        try 
            if ( close ) 
                cancelledKey(sk);
             else if ( sk.isValid() && attachment != null ) 
                if (sk.isReadable() || sk.isWritable() ) 
                    if ( attachment.getSendfileData() != null ) 
                        processSendfile(sk,attachment, false);
                     else 
                        unreg(sk, attachment, sk.readyOps());
                        boolean closeSocket = false;
                        // Read goes before write
                        if (sk.isReadable()) 
                        	// 核心代码
                            if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) 
                                closeSocket = true;
                            
                        
                        if (!closeSocket && sk.isWritable()) 
                            if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) 
                                closeSocket = true;
                            
                        
                        if (closeSocket) 
                            cancelledKey(sk);
                        
                    
                
             else 
                //invalid key
                cancelledKey(sk);
            
         catch ( CancelledKeyException ckx ) 
            cancelledKey(sk);
         catch (Throwable t) 
            ExceptionUtils.handleThrowable(t);
            log.error("",t);
        
    

看上面的核心代码,sk.isReadable(),可以看出来当请求可以处理时会进入,然后执行processSocket

public boolean processSocket(SocketWrapperBase<S> socketWrapper,
        SocketEvent event, boolean dispatch) 
    try 
        if (socketWrapper == null) 
            return false;
        
        // 核心代码
        SocketProcessorBase<S> sc = processorCache.pop();
        if (sc == null) 
            sc = createSocketProcessor(socketWrapper, event);
         else 
            sc.reset(socketWrapper, event);
        
        Executor executor = getExecutor();
        if (dispatch && executor != null) 
        	// 核心代码,放到线程池里面执行
            executor.execute(sc);
         else 
            sc.run();
        
     catch (RejectedExecutionException ree) 
        getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
        return false;
     catch (Throwable t) 
        ExceptionUtils.handleThrowable(t);
        // This means we got an OOM or similar creating a thread, or that
        // the pool and its queue are full
        getLog().error(sm.getString("endpoint.process.fail"), t);
        return false;
    
    return true;

这里的核心类,拿到一个SocketProcessorBase后有线程池则放到线程池里面去执行,则说明SocketProcessorBase也是一个线程类,相当于又把请求交给了SocketProcessorBase的run方法去处理了

SocketProcessorBase的run()

  @Override
    public final void run() 
        synchronized (socketWrapper) 
            
            if (socketWrapper.isClosed()) 
                return;
            
            // 核心代码
            doRun();
        
    

执行doRun()
接下去执行了几十个类的转发请求,我们不做详细解释,我们直接找到关键类ApplicationFilterChain的internalDoFilter方法,

  private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException 

        ......
             else 
            	// 核心代码
                servlet.service(request, response);
            
         catch (IOException | ServletException | RuntimeException e) 
            throw e;
         catch (Throwable e) 
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.servlet"), e);
         finally 
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) 
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            
        
    

我们在核心代码servlet.service(request, response)出打上断点,我们就可以看到我们上面省略的所有流程,即从SocketProcessorBase的run执行到ApplicationFilterChain的internalDoFilter方法的详细流程了


这里就可以看到我们的servlet的具体实现类就是我们要找的DispatchServlet了,执行DispatchServlet的service方法,并且传入了request和response

经过一些转发会进入DispatchServlet的父类FrameworkServlet的service方法

 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        if (httpMethod != HttpMethod.PATCH && httpMethod != null) 
        	// 核心方法
            super.service(request, response);
         else 
            this.processRequest(request, response);
        

    

然后进入了HttpServlet的service,在里面根据HttpServletRequest的请求method类型,当前我们是get请求

 protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException 

        String method = req.getMethod();

		// 核心方法,根据不同method进入不同的处理
        if (method.equals(METHOD_GET)) 
        	if (lastModified == -1) 
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                // 进入get方法
                doGet(req, resp);
             
            ......

         else if (method.equals(METHOD_HEAD)) 
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

         else if (method.equals(METHOD_POST)) 
            doPost(req, resp);

         else if (method.equals(METHOD_PUT)) 
            doPut(req, resp);

         else if (method.equals(METHOD_DELETE)) 
            doDelete(req, resp);

         else if (method.equals(METHOD_OPTIONS)) 
            doOptions(req,resp);

         else if (method.equals(METHOD_TRACE)) 
            doTrace(req,resp);

         else 
           ......
        
    

进入FrameworkServlet的doGet方法

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    this.processRequest(request, response);

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    ......

    try 
    	// 核心方法
        this.doService(request, response);
     catch (IOException | ServletException var16) 
        failureCause = var16;
        throw var16;
     catch (Throwable var17) 
        failureCause = var17;
        throw new NestedServletException("Request processing failed", var17);
     finally 
        this.resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) 
            requestAttributes.requestCompleted();
        

        this.logResult(request, response, (Throwable)failureCause, asyncManager);
        this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
    


this.doService(request, response),然后就进入了我们的DispatcherServlet的doService,至此我们的请求成功的被tomcat转发到了DispatcherServlet的doService方法

总结

我们从源码层面从头到尾分析了一遍tomcat的启动流程和tomcat处理http请求的全过程,可以看到虽然省略了很多详细的代码,但是整体流程还是相对清晰的
主要流程是
1、根据配置信息启动了一个ServerSocket
2、启动了多个Poller线程类来接受请求
3、接受到http请求后扔给SocketProcessorBase线程来处理
4、SocketProcessorBase执行后最终把请求扔到了DispatcherServlet的doService方法处理
5、doService基于spring mvc调用Controller请求并且返回数据给浏览器渲染

以上是关于springboot内置tomcat启动全流程详解及处理请求详解的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot配置外部Tomcat项目启动流程源码分析(下)

SpringBoot配置外部Tomcat项目启动流程源码分析(上)

SpringBoot配置外部Tomcat项目启动流程源码分析(上)

SpringBoot内置tomcat启动原理

SpringBoot内置tomcat启动过程及原理

SpringBoot应用启动内置Tomcat的过程分析