spring-session-redis一个有意思的问题引起的源码追溯

73 阅读18分钟

在工作中,后台程序需要提供一些接口供很多设备终端去请求程序版本用于升级,从而检查自己是否需要升级,此为本篇文章的背景.

首先介绍下我们的程序,我们的后台程序是一个单机程序,部署在很多公司的服务器上.程序使用spring-session-data-redis管理session,不知从哪一天起,我发现redis里的session相关的文件有几十万条,由于spring-session不是我引入的,也没怎么细了解过,当时就觉得很奇怪,于是花费了一段时间研究了下.

一、spring-session是如何和我们的请求产生关联的,他是如何知道何时在redis中存储上session以及给更新session的.抱着这个疑问,我开始追溯源码.为了了解前端过来的请求是如何与sping-session产生关联的,我决定先研究下springboot是怎么启动内嵌的tomcat的,并大概是如何接收请求.

让我们从springboot的启动类开始追踪吧. a15ddfa7718598fbf5c2fb42a9dfd953.png

图1

071aef600bfb2c14d97b832a8a771dbb.png

图2

349b38f8d10a670ee1fd13f21c15c36c.png

图3

image.png

图4

顺着启动类的run方法,我们追踪到了图4,来到了SpringApplication的run方法,这个方法通过createApplicationContext创建了一个spring容器,紧接着调用refreshContext(context)对这个容器执行了初始化操作,并设置了容器shutdown的Hook钩子 image.png

图5

点进去createApplicationContext方法发现用反射创建了一个 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext容器. 顺着图3的new SpringAppplication进去看下,debug发现返回的是SERVLET,至于为什么,后续追究一下,此处不细看了.

image.png

图6

private void refreshContext(ConfigurableApplicationContext context) {
   refresh(context);
   if (this.registerShutdownHook) {
      try {
         context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
         // Not allowed in some environments.
      }
   }
}

ConfigurableApplicationContext.registerShutdownHook->AbstractApplicationContext.registerShutdownHook

public void registerShutdownHook() {
   if (this.shutdownHook == null) {
      // No shutdown hook registered yet.
      this.shutdownHook = new Thread() {
         @Override
         public void run() {
            synchronized (startupShutdownMonitor) {
               //做spring容器的关闭操作,以及WebServer的关闭操作
               doClose();
            }
         }
      };
      //将shutdownHook添加到虚拟机关机的钩子里执行,我在写GB32960时候直接用下面代码写过虚拟机关机时
      //将内存数据保存到redis中去,然后程序启动再去加载到内存中来
      Runtime.getRuntime().addShutdownHook(this.shutdownHook);
   }
}

image.png

图7

至此,我们跟随源码的createApplicationContext创建了一个AnnotationConfigServletWebServerApplicationContext容器,下图是该容器的继承结构图.可以看出AnnotationConfigServletWebServerApplicationContext结合了springApplicationContext的体系结构针对servlet容器做出的封装,生成的AnnotationConfigServletWebServerApplicationContext实例的refreshContext(context)其实是调用AbstractApplicationContextrefresh

4310a2cd60c5ab70e6ea3bc801e20b9e.png

图8

AbstractApplicationContextrefresh方法有一步onRefresh方法留待子类去实现,从而将子类需要的相关bean注入进spring容器中

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.
         //在特定上下文子类中初始化其他特殊bean。
         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();
      }
   }
}

AnnotationConfigServletWebServerApplicationContext的父类ServletWebServerApplicationContext中实现了这个方法如下所示,可以看到实现的方法里继续执行了createWebServer方法(这个方法就是我们要追究的tomcat服务何时被创建的)

protected void onRefresh() {
   super.onRefresh();
   try {
      //创建一个WebServer服务器.我们即将看到tomcat被创建出来
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

让我们来大概看下createWebServer的流程.

9076bfd22383428bc486c8e12a61e85d.png

图9

首先通过getWebServerFactory方法去获取spring容器中ServletWebServerFactory的bean(在springboot自动配置类ServletWebServerFactoryAutoConfiguration文章中我们详细讲述了SpringBoot的自动配置类是如何注入了一个TomcatServletWebServerFactory的,这一块不清楚的话请回顾下那篇博客)

2240df4c2ff1b5c3a4ba0586861ce2d4.png

图10

继续来看getSelfInitializer方法,这个方法返回一个函数式接口ServletContextInitializer的方法引用,该方法引用指向的是ServletWebServerApplicationContext.selfInitialize方法,这个方法是去获取spring容器中的所有ServletContextInitializer类(子类有ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBean,DelegatingFilterProxyRegistrationBean),并去执行他们的onStartup操作

image.png

图11

跟着上图所示来看getServletContextInitializerBeans方法如下

	 * Returns {@link ServletContextInitializer}s that should be used with the embedded
	 * web server. By default this method will first attempt to find
	 * {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
	 * {@link EventListener} beans.
	 * @return the servlet initializer beans
	 */
	protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
//ServletContextInitializerBeans继承了AbstractCollection<ServletContextInitializer>,
//内部提供iterator方法获取了成员变量sortedList的iterator用于迭代
		return new ServletContextInitializerBeans(getBeanFactory());
	}

继续来看new ServletContextInitializerBeans(ListableBeanFactory beanFactory)做了什么,

public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		this.initializers = new LinkedMultiValueMap<>();
//核心方法,见下面 主要是将容器中的ServletContextInitializer bean全添加到initializers中去
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = new ArrayList<>();
//将initializers中的contextInitializerslist排序并全部添加到sortedInitializers中去
		this.initializers.values().forEach((contextInitializers) -> {
			AnnotationAwareOrderComparator.sort(contextInitializers);
			sortedInitializers.addAll(contextInitializers);
		});
//sortedInitializers不可被修改
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
	}

	private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
//getOrderedBeansOfType从sping容器中获取ServletContextInitializer bean的Entry
		for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
				beanFactory, ServletContextInitializer.class)) {
//核心代码,见下面
			addServletContextInitializerBean(initializerBean.getKey(),
					initializerBean.getValue(), beanFactory);
		}
	}

	private void addServletContextInitializerBean(String beanName,
			ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
//各种类型的ServletContextInitializer调用addServletContextInitializerBean方法,见下面
		if (initializer instanceof ServletRegistrationBean) {
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
			addServletContextInitializerBean(Servlet.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer)
					.getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
					.getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer,
					beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName,
					initializer, beanFactory, initializer);
		}
	}

	private void addServletContextInitializerBean(Class<?> type, String beanName,
			ServletContextInitializer initializer, ListableBeanFactory beanFactory,
			Object source) {
//添加到initializers中去
		this.initializers.add(type, initializer);
		if (source != null) {
			// Mark the underlying source as seen in case it wraps an existing bean
			this.seen.add(source);
		}
		if (ServletContextInitializerBeans.logger.isDebugEnabled()) {
			String resourceDescription = getResourceDescription(beanName, beanFactory);
			int order = getOrder(initializer);
			ServletContextInitializerBeans.logger.debug("Added existing "
					+ type.getSimpleName() + " initializer bean '" + beanName
					+ "'; order=" + order + ", resource=" + resourceDescription);
		}
	}

getSelfInitializer到此结束,我们回到图9继续下一步factory.getWebServer 执行的是ServletWebServerFactory中的getWebServer方法,spring容器中的ServletWebServerFactory bean是TomcatServletWebServerFactory,我们看下它的getWebServer方法

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
//原生操作创建Tomcat,此处不深究了
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null ? this.baseDirectory
         : createTempDir("tomcat"));
   tomcat.setBaseDir(baseDir.getAbsolutePath());
//原生操作创建Connector,此处不深究了
   Connector connector = new Connector(this.protocol);
//获取通过getServer去获取Service,没获取到Server就会去创建一个新的Server和Service
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
//getEngine()获取Engine,获取不到就创建一个并塞入到Service
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
//执行前置initializers
   prepareContext(tomcat.getHost(), initializers);
//核心代码
   return getTomcatWebServer(tomcat);
}
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
   File documentRoot = getValidDocumentRoot();
   TomcatEmbeddedContext context = new TomcatEmbeddedContext();
   if (documentRoot != null) {
      context.setResources(new LoaderHidingResourceRoot(context));
   }
   context.setName(getContextPath());
   context.setDisplayName(getDisplayName());
   context.setPath(getContextPath());
   File docBase = (documentRoot != null ? documentRoot
         : createTempDir("tomcat-docbase"));
   context.setDocBase(docBase.getAbsolutePath());
   context.addLifecycleListener(new FixContextListener());
   context.setParentClassLoader(
         this.resourceLoader != null ? this.resourceLoader.getClassLoader()
               : ClassUtils.getDefaultClassLoader());
   resetDefaultLocaleMapping(context);
   addLocaleMappings(context);
   context.setUseRelativeRedirects(false);
   configureTldSkipPatterns(context);
   WebappLoader loader = new WebappLoader(context.getParentClassLoader());
   loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
   loader.setDelegate(true);
   context.setLoader(loader);
   if (isRegisterDefaultServlet()) {
      addDefaultServlet(context);
   }
   if (shouldRegisterJspServlet()) {
      addJspServlet(context);
      addJasperInitializer(context);
   }
   context.addLifecycleListener(new StaticResourceConfigurer(context));
//合并成数组
   ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
   host.addChild(context);
//核心方法,用于将各种ServletContextInitializer数组设置到TomcatStarter启动器中,
//猜测Tomcat启动后会触发TomcatStarter执行ServletContextInitializer数组中初始化动作
   configureContext(context, initializersToUse);
//一个关于tomcat的后置处理接口,暂时没有实现,可以自己实现去做一些钩子操作
   postProcessContext(context);
}

/**
 * Configure the Tomcat {@link Context}.
 * @param context the Tomcat context
 * @param initializers initializers to apply
 */
protected void configureContext(Context context,
      ServletContextInitializer[] initializers) {
      //创建TomcatStarter启动器,将上文得到的ServletContextInitializer设置进去
   TomcatStarter starter = new TomcatStarter(initializers);
   if (context instanceof TomcatEmbeddedContext) {
      // Should be true
//将TomcatStarter启动器设置到tomcat容器context中去
      ((TomcatEmbeddedContext) context).setStarter(starter);
   }
//将TomcatStarter塞入到StandardContext的initializers Map中去,在StandardContext执行继承自
//LifecycleBase的startInternal方法初始化时,会调用TomcatStarter的onStartup
   context.addServletContainerInitializer(starter, NO_CLASSES);
//Tomcat生命周期的一些监听器塞入到Tomcat上下文中,暂不深究
   for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
      context.addLifecycleListener(lifecycleListener);
   }
   for (Valve valve : this.contextValves) {
      context.getPipeline().addValve(valve);
   }
   for (ErrorPage errorPage : getErrorPages()) {
      new TomcatErrorPage(errorPage).addToContext(context);
   }
   for (MimeMappings.Mapping mapping : getMimeMappings()) {
      context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
   }
   configureSession(context);
//List<TomcatContextCustomizer> tomcatContextCustomizers,TomcatContextCustomizer
//是关于Tomcat上下文的自定义器,是一个函数式接口,写到这个地方我还没有发现在哪儿被使用过.暂不深究
   for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
//将tomcat容器设置到customizer
      customizer.customize(context);
   }
}
class TomcatStarter implements ServletContainerInitializer {

   private static final Log logger = LogFactory.getLog(TomcatStarter.class);

   private final ServletContextInitializer[] initializers;

   private volatile Exception startUpException;

   TomcatStarter(ServletContextInitializer[] initializers) {
      this.initializers = initializers;
   }

//未深究此方法什么时候被调用.看名字应该是servlet容器生命周期的钩子触发执行
   @Override
   public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
         throws ServletException {
      try {
//TomcatStarter实现了ServletContainerInitializer,onStartup方法被调用时候执行所有
//ServletContextInitializer的onStartup操作
         for (ServletContextInitializer initializer : this.initializers) {
            initializer.onStartup(servletContext);
         }
      }
      catch (Exception ex) {
         this.startUpException = ex;
         // Prevent Tomcat from logging and re-throwing when we know we can
         // deal with it in the main thread, but log for information here.
         if (logger.isErrorEnabled()) {
            logger.error("Error starting Tomcat context. Exception: "
                  + ex.getClass().getName() + ". Message: " + ex.getMessage());
         }
      }
   }

   public Exception getStartUpException() {
      return this.startUpException;
   }

}

再次回到TomcatServletWebServerFactorygetWebServer方法,接着看核心方法getTomcatWebServer,前面经过主要设置了一些Tomcat上下文的东西,接下来就是Tomcat服务的启动了

/**
 * Factory method called to create the {@link TomcatWebServer}. Subclasses can
 * override this method to return a different {@link TomcatWebServer} or apply
 * additional processing to the Tomcat server.
 * @param tomcat the Tomcat server.
 * @return a new {@link TomcatWebServer} instance
 */
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();
}
private void initialize() throws WebServerException {
   TomcatWebServer.logger
         .info("Tomcat initialized with port(s): " + getPortsDescription(false));
   synchronized (this.monitor) {
      try {
         addInstanceIdToEngineName();

         Context context = findContext();
         context.addLifecycleListener((event) -> {
            if (context.equals(event.getSource())
                  && Lifecycle.START_EVENT.equals(event.getType())) {
               // Remove service connectors so that protocol binding doesn't
               // happen when the service is started.
               removeServiceConnectors();
            }
         });

//核心方法 启动服务器以触发初始化侦听器
         // 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);
      }
   }
}
/**
 * Start the server.
 *
 * @throws LifecycleException Start error
 */
public void start() throws LifecycleException {
//原生Tomcat,暂未深究
    getServer();
//原生Tomcat,暂未深究
    getConnector();
核心方法
    server.start();
}
    /**
     * Get the server object. You can add listeners and few more
     * customizations. JNDI is disabled by default.
     * @return The Server
     */
public Server getServer() {

    if (server != null) {
        return server;
    }

    System.setProperty("catalina.useNaming", "false");
//创建一个标准的Server
    server = new StandardServer();

    initBaseDir();

    server.setPort( -1 );
//创建一个标准的Service
    Service service = new StandardService();
    service.setName("Tomcat");
    server.addService(service);
    return server;
}
    /**
     * Get the default http connector. You can set more
     * parameters - the port is already initialized.
     *
     * <p>
     * Alternatively, you can construct a Connector and set any params,
     * then call addConnector(Connector)
     *
     * @return A connector object that can be customized
     */
    public Connector getConnector() {
//获取上文的Service并将创建的Connector添加到该Service
        Service service = getService();
        if (service.findConnectors().length > 0) {
            return service.findConnectors()[0];
        }

        if (defaultConnectorCreated) {
            return null;
        }
        // The same as in standard Tomcat configuration.
        // This creates an APR HTTP connector if AprLifecycleListener has been
        // configured (created) and Tomcat Native library is available.
        // Otherwise it creates a NIO HTTP connector.
//默认协议是HTTP1.1
        Connector connector = new Connector("HTTP/1.1");
        connector.setPort(port);
        service.addConnector(connector);
        defaultConnectorCreated = true;
        return connector;
    }

主要来看server.start()方法,下图为StandardServer的继承结构图,实际执行的是LifeCycleBase的start方法

image.png

图12

@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;
    }
//根据所处生命周期的状态决定是init还是Stop
    if (state.equals(LifecycleState.NEW)) {
//核心代码1
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
//关闭Tomcat时的操作
        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();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    }
}

5677d37dfe59a68961e6dd77a890dbfb.png

图13

initInterna这个方法被许多类实现,这儿我们只讨论StandardServer,StandardService和Connecotr三个

image.png

图14

如图14所示,由于我们最初执行的是StandardServer.start方法进来的initInternal方法,经由这个堆栈可以看出来整个启动过程中会一次经历StandardServer的initInternal方法,在StandardServer的initInternal方法中又会去调用自身成员对象数组Service services的所有Service的initInternal方法,在执行到StandardService的initInternal方法内部时,又会执行它的成员对象数组Connector connectors的initInternal方法(其中StandardServer的initInternal还会去执行NamingResourcesImpl globalNamingResources的initInternal方法,StandardService的initInternal中还会去执行Engine engine,Executor findExecutors(),MapperListener mapperListener的initInternal方法,此处不深究了) 另外还有关于

image.png

图15

在Connector的initInternal方法里执行协议处理器的初始化操作

image.png

图16

然后会执行父类AbstractProtocol的init方法,见下图

image.png

图17

其中AbstractProtocol中关于endpoint的说明如下:

/**
 * Endpoint that provides low-level network I/O - must be matched to the
 * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
 * Endpoint etc.).
 */
//提供低等级网络IO的Endpoint-必须匹配ProtocolHandler实现.(ProtocolHandler使用NIO,需要NIO类型的Endpoint)
private final AbstractEndpoint<S> endpoint;

NioEndpoint中未重写init方法,因此执行父类AbstractJsseEndpoint里的init方法,最终执行的是AbstractEndpoint的init方法.

@Override
public void init() throws Exception {
    testServerCipherSuitesOrderSupport();
    super.init();
}
    public void init() throws Exception {
        if (bindOnInit) {
//核心方法,涉及到,但是此时的bindOnInit为false,并不会执行,后续会执行到这个bind方法
            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方法在三个类中有实现,我们此处用到的是NioEndpoint类里面的bind.后续涉及到再说这个bind方法

接下来我们接着来看上文提到的核心代码startInternal方法. 这个过程类似上面的initInternal过程,也是经由StandardServer的startInternal执行到StandardService的startInternal,这儿有一个不理解的现象

@Override
protected void startInternal() throws LifecycleException {

    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);

    // Start our defined Container first
    if (engine != null) {
        synchronized (engine) {
//执行Standard的startInternal方法->执行父类ContainerBase的startInternal方法,这里面会执行Cluster(此时为null),
//Realm的start操作,以及一个Container数组的start操作(数组内是一个StandardHost对象,继承结构如图18,这个对象会被
//放入到线程池中去执行它继承自LifecycleBase的start方法-> init() & startInternal从而初始化).
//还会执行threadStart开启一个后台线程一直执行ContainerBackgroundProcessor任务类,
//任务类里面执行processChildren操作,暂未深究,需要比较Tomcat的架构和知识.
            engine.start();
        }
    }

    synchronized (executors) {
//此时为空集合
        for (Executor executor: executors) {
            executor.start();
        }
    }
//执行MapperListener的start操作
    mapperListener.start();

    // Start our defined Connectors second
    synchronized (connectorsLock) {
//这个时候的connectors数组为空数组,可是我们前面执行StandardService的initInternal方法时候也便利了这个数
//组(见图18),那时候里面是有一个Connector对象的(上面刚说完这个过程的),怎么到这就没了,中间也没发现哪儿有操作把
//它去掉了.暂时放下不深究,后续再查查
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardService.connector.startFailed",
                        connector), e);
            }
        }
    }
}

image.png

图18

image.png

图19

至此Tomcat的启动流程告一段落.由于本文主要描述的是Springboot如何内嵌Tomcat并启动的流程,所以上面的描述只是涉及到Tomcat的start方法引起的后续流程,至于Tomcat其他的生命周期的一系列源码流程,后续可以再看看.

上面提到的NioEndpoint中的bind方法在AbstractApplicationContext执行refresh方法到finishRefresh方法时会触发,来看下大概流程

创建的AnnotationConfigServletWebServerApplicationContext中未实现finishRefresh方法,实际执行的是ServletWebServerApplicationContext的finishRefresh方法,如下

@Override
protected void finishRefresh() {
//调用AbstractApplicationContext的finishRefresh如下图20所示
   super.finishRefresh();
   WebServer webServer = startWebServer();
   if (webServer != null) {
//发布事件ServletWebServerInitializedEvent
      publishEvent(new ServletWebServerInitializedEvent(webServer, this));
   }
}

AbstractApplicationContext的finishRefresh方法如下:

image.png

图20

继续来看ServletWebServerApplicationContext的startWebServer方法

private WebServer startWebServer() {

//此处的webServer就是上文提到的ServletWebServerApplicationContext.onRefresh里调用
//createWebServer的WebServer
   WebServer webServer = this.webServer;
   if (webServer != null) {
      webServer.start();
   }
   return webServer;
}

此处不一一细表了.列出大概的流程吧. WebServer.start()->TomcatWebServer.start()->addPreviouslyRemovedConnectors()->StandardService.addConnector()->Connector.start()未实现,采用父类LifeCycleBase.start()-> startInternal()->Connector.startInternal()->ProtocolHandler.start()->AbstractProtocol.start()->AbstractEndpoint.start()->bind()->NioEndpoint.bind();


public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
        bind();
        bindState = BindState.BOUND_ON_START;
    }
//核心方法
    startInternal();
}

下面我们先来看下NioEndpoint.bind()做了什么;

/**
 * Initialize the endpoint.
 */
@Override
public void bind() throws Exception {
//以下为java原生NIO Api操作,java原生NIO其实有许多坑的.netty完美封装了JAVA原生IO
//核心方法,根据环境决定使用Select还是epoll
    serverSock = ServerSocketChannel.open();
    socketProperties.setProperties(serverSock.socket());
    InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
    serverSock.socket().bind(addr,getAcceptCount());
    serverSock.configureBlocking(true); //mimic APR behavior

    // 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;
    }
//此处设置的StopLatch在Poll任务的run方法结束while循环后会执行一次countDown,暂时不理解含义
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();
//待细看,未追究
    selectorPool.open();
}
public static ServerSocketChannel open() throws IOException {
//看下SelectorProvider.provider()方法
    return SelectorProvider.provider().openServerSocketChannel();
}
/**
 *返回此Java虚拟机调用的系统范围默认选择器提供程序.window-Selector  Linux-Epoll
 * Returns the system-wide default selector provider for this invocation of
 * the Java virtual machine.
 */
public static SelectorProvider provider() {
    synchronized (lock) {
        if (provider != null)
            return provider;
        return AccessController.doPrivileged(
            new PrivilegedAction<SelectorProvider>() {
                public SelectorProvider run() {
                        if (loadProviderFromProperty())
                            return provider;
                        if (loadProviderAsService())
                            return provider;
//核心方法
                        provider = sun.nio.ch.DefaultSelectorProvider.create();
                        return provider;
                    }
                });
    }
}
package sun.nio.ch;

import java.nio.channels.spi.SelectorProvider;

public class DefaultSelectorProvider {
    private DefaultSelectorProvider() {
    }

    public static SelectorProvider create() {
//由于我是windows环境,所以次数返回的是windows的Selector Provider.
        return new WindowsSelectorProvider();
    }
}

bind方法最终根据虚拟机所处的操作系统环境选择了Windows的Selector Provider机制.如果是linux上的Java环境,返回的就是EPollSelectorProvider了.

接着来看最重要的AbstractEndpoint.startInternal方法

/**
 * Start the NIO endpoint, creating acceptor, poller threads.
 */
@Override
public void startInternal() throws Exception {

    if (!running) {
        running = true;
        paused = false;

        processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getProcessorCache());
        eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
        nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getBufferPool());

        // Create worker collection
        if ( getExecutor() == null ) {
            createExecutor();
        }

//创建了一个LimitLatch,在LimitLatch的构造中创建了一个Sync(该类实现了抽象队列同步化器AQS,未理解含义)
        initializeConnectionLatch();

        // Start poller threads
        pollers = new Poller[getPollerThreadCount()];
//启动pollerThreadCount个Poll线程,最少为2个
        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();
        }
//acceptor模型的Acceptor调度线程,默认为1个,将一个个连接过来的连接请求分配给poller线程去执行
        startAcceptorThreads();
    }
}

来看下Acceptor调度线程干了啥

@Override
public void run() {

    int errorDelay = 0;

    // Loop until we receive a shutdown command
    while (running) {

        // Loop if endpoint is paused
        while (paused && running) {
            state = AcceptorState.PAUSED;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // Ignore
            }
        }

        if (!running) {
            break;
        }
        state = AcceptorState.RUNNING;

        try {
            //if we have reached max connections, wait
            countUpOrAwaitConnection();

            SocketChannel socket = null;
            try {
                // Accept the next incoming connection from the server
                // socket
//从server sockert接收接入的连接
                socket = serverSock.accept();
            } catch (IOException ioe) {
                // We didn't get a socket
                countDownConnection();
                if (running) {
                    // Introduce delay if necessary
                    errorDelay = handleExceptionWithDelay(errorDelay);
                    // re-throw
                    throw ioe;
                } else {
                    break;
                }
            }
            // Successful accept, reset the error delay
            errorDelay = 0;

            // Configure the socket
            if (running && !paused) {
                // setSocketOptions() will hand the socket off to
                // an appropriate processor if successful
//将过来的连接事件交给poller去处理
                if (!setSocketOptions(socket)) {
                    closeSocket(socket);
                }
            } else {
                closeSocket(socket);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("endpoint.accept.fail"), t);
        }
    }
    state = AcceptorState.ENDED;
}

看下setSocketOptions

/**
 * Process the specified connection.
 * @param socket The socket channel
 * @return <code>true</code> if the socket was correctly configured
 *  and processing may continue, <code>false</code> if the socket needs to be
 *  close immediately
 */
protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //disable blocking, APR style, we are gonna be polling it
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);

        NioChannel channel = nioChannels.pop();
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
            channel.setIOChannel(socket);
            channel.reset();
        }
//获取Poller线程去registerChannel事件
        getPoller0().register(channel);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        try {
            log.error("",t);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
        // Tell to close the socket
        return false;
    }
    return true;
}
        /**
         * Registers a newly created socket with the poller.
         *
         * @param socket    The newly created socket
         */
        public void register(final NioChannel socket) {
            socket.setPoller(this);
            NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
            socket.setSocketWrapper(ka);
            ka.setPoller(this);
            ka.setReadTimeout(getSocketProperties().getSoTimeout());
            ka.setWriteTimeout(getSocketProperties().getSoTimeout());
            ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
            ka.setSecure(isSSLEnabled());
            ka.setReadTimeout(getConnectionTimeout());
            ka.setWriteTimeout(getConnectionTimeout());
            PollerEvent r = eventCache.pop();
            ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
            if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
            else r.reset(socket,ka,OP_REGISTER);
//封装成PollerEvent(实现了Runnable)塞入到Poller的成员变量events 同步队列中去,events里面的任务在Poller的run方里会不断被拿出来执行.来看下poller的run方法
            addEvent(r);
        }
**
 * The background thread that adds sockets to the Poller, checks the
 * poller for triggered events and hands the associated socket off to an
 * appropriate processor as events occur.
 */
@Override
public void run() {
    // Loop until destroy() is called
    while (true) {

        boolean hasEvents = false;

        try {
            if (!close) {
//events获取events同步队列里的PollerEvent任务并执行
                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());

//遍历selector上面的IO事件执行processKey方法
        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();
//核心方法,暂时不能明确理解干嘛的
                processKey(sk, attachment);
            }
        }//while

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

    getStopLatch().countDown();
}

以下为Poller的events方法

/**
 * Processes events in the event queue of the Poller.
 *
 * @return <code>true</code> if some events were processed,
 *   <code>false</code> if queue was empty
 */
public boolean events() {
    boolean result = false;

    PollerEvent pe = null;
    for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
        result = true;
        try {
//执行PollerEventr任务
            pe.run();
            pe.reset();
            if (running && !paused) {
                eventCache.push(pe);
            }
        } catch ( Throwable x ) {
            log.error("",x);
        }
    }

    return result;
}

由于知识储备不够,processKey后的方法不再一一追踪细说了,此处记录下大概的流程,后续再来补写细节优化. Poller.processKey->AbstractEndpoint.processSocket

/**
 * Process the given SocketWrapper with the given status. Used to trigger
 * processing as if the Poller (for those endpoints that have one)
 * selected the socket.
 *
 * @param socketWrapper The socket wrapper to process
 * @param event         The socket event to be processed
 * @param dispatch      Should the processing be performed on a new
 *                          container thread
 *
 * @return if processing was triggered successfully
 */
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
        SocketEvent event, boolean dispatch) {
    try {
        if (socketWrapper == null) {
            return false;
        }
        SocketProcessorBase<S> sc = processorCache.pop();
        if (sc == null) {
//createSocketProcessor 方法将 Socket 封装到 SocketProcessor中, SocketProcessor实现了Runnable接口。
//Executor的worker 线程通过调用其 run方法来对 Socket进行处理
            sc = createSocketProcessor(socketWrapper, event);
        } else {
            sc.reset(socketWrapper, event);
        }
        Executor executor = getExecutor();
        if (dispatch && executor != null) {
//执行SocketProcessor的run方法处理Socket
            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.run->doRun->SocketProcessor.doRun)

        @Override
        protected void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

            try {
                int handshake = -1;

                try {
                    if (key != null) {
                        if (socket.isHandshakeComplete()) {
                            // No TLS handshaking required. Let the handler
                            // process this socket / event combination.
                            handshake = 0;
                        } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                                event == SocketEvent.ERROR) {
                            // Unable to complete the TLS handshake. Treat it as
                            // if the handshake failed.
                            handshake = -1;
                        } else {
                            handshake = socket.handshake(key.isReadable(), key.isWritable());
                            // The handshake process reads/writes from/to the
                            // socket. status may therefore be OPEN_WRITE once
                            // the handshake completes. However, the handshake
                            // happens when the socket is opened so the status
                            // must always be OPEN_READ after it completes. It
                            // is OK to always set this as it is only used if
                            // the handshake completes.
                            event = SocketEvent.OPEN_READ;
                        }
                    }
                } catch (IOException x) {
                    handshake = -1;
                    if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
                } catch (CancelledKeyException ckx) {
                    handshake = -1;
                }
                if (handshake == 0) {
                    SocketState state = SocketState.OPEN;
                    // Process the request from this socket
                    if (event == null) {
//获取Handler(获取的应该是AbstractHttp11Protocol构造中塞入的ConnectionHandler)处理器:Servlet(处理器),
//调度处理器的处理方法
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
                        state = getHandler().process(socketWrapper, event);
                    }
                    if (state == SocketState.CLOSED) {
                        close(socket, key);
                    }
                } else if (handshake == -1 ) {
                    close(socket, key);
                } else if (handshake == SelectionKey.OP_READ){
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
                socket.getPoller().cancelledKey(key);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error("", t);
                socket.getPoller().cancelledKey(key);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && !paused) {
                    processorCache.push(this);
                }
            }
        }