Spring WebFlux 工作原理分析 - 2.应用启动过程--5.启动Web服务器

本文深入剖析ReactiveWeb服务器的启动过程,包括NettyWebServer的启动、启动线程的阻塞等待以及HttpHandler的自动配置机制。揭示了NettyWebServer如何处理请求,以及HttpHandler组件的构建细节。

在本系列的上一篇文章中,我们介绍了Reactive Web服务器NettyWebServer的创建,这一篇文章,我们讲解该Reactive Web服务器是如何启动的。它对应于AnnotationConfigReactiveWebServerApplicationContext基类ReactiveWebServerApplicationContext的方法
startReactiveWebServer,被finishRefresh方法在缺省流程逻辑后调用:

// ReactiveWebServerApplicationContext 代码片段
    @Override
	protected void finishRefresh() {
		super.finishRefresh(); // 调用基类定义的通用 finishRefresh 逻辑
        
        // 启动 Reactive Web 服务器,并发布相应事件
        // 这是 ReactiveWebServerApplicationContext 针对 WebFlux 环境的扩展逻辑
		WebServer webServer = startReactiveWebServer();
		if (webServer != null) {
			publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
		}
	}

方法startReactiveWebServer实现如下 :

// ReactiveWebServerApplicationContext 代码片段
	private WebServer startReactiveWebServer() {
		ServerManager serverManager = this.serverManager;
       // 使用静态方法  ServerManager#start 启动 Reactive WebServer,
       // HttpHandler 由当前应用上下文的方法 getHttpHandler 提供
		ServerManager.start(serverManager, this::getHttpHandler);
       // 获取  serverManager 的属性 server 
		return ServerManager.getWebServer(serverManager);
	}

startReactiveWebServer方法实现有两个要点 :

  1. 真正的启动工作通过调用ServerManager.start来完成;
  2. 调用ServerManager.start时其参数Supplier<HttpHandler>使用当前ReactiveWebServerApplicationContext实例的方法getHttpHandler

我们逐一来分析。

1. ServerManager#start

    // ReactiveWebServerApplicationContext 内嵌类 ServerManager 代码片段
        public static void start(ServerManager manager, Supplier<HttpHandler> handlerSupplier) {
			if (manager != null && manager.server != null) {
              // 将指定 ServerManager manager 的属性 handler设置成参数 handlerSupplier 所指定的
              // HttpHandler
				manager.handler = handlerSupplier.get();
              // 调用 指定 ServerManager manager 中的 WebServer server 的启动逻辑 
				manager.server.start();
			}
		}

这里关于WebServer#start的启动,需要参考NettyWebServer :

package org.springframework.boot.web.embedded.netty;

// 省略 import 行

public class NettyWebServer implements WebServer {

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

	private final HttpServer httpServer;

	private final ReactorHttpHandlerAdapter handlerAdapter;

	private final Duration lifecycleTimeout;

	private DisposableServer disposableServer;

    // 构造函数
    // 从上一篇的分析我们已经知道,这里 ReactorHttpHandlerAdapter 其实包装了一个 HttpHandler,
    // 而该 HttpHandler 来自应用上下文对象 ReactiveWebServerApplicationContext 的方法 getHttpHandler
	public NettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter, 
			Duration lifecycleTimeout) {
		Assert.notNull(httpServer, "HttpServer must not be null");
		Assert.notNull(handlerAdapter, "HandlerAdapter must not be null");
		this.httpServer = httpServer;
		this.handlerAdapter = handlerAdapter;
		this.lifecycleTimeout = lifecycleTimeout;
	}

	@Override
	public void start() throws WebServerException {
		if (this.disposableServer == null) {
			try {
              // 启动 Reactive Web Server  
				this.disposableServer = startHttpServer();
			}
			catch (Exception ex) {
				ChannelBindException bindException = findBindException(ex);
				if (bindException != null) {
					throw new PortInUseException(bindException.localPort());
				}
				throw new WebServerException("Unable to start Netty", ex);
			}
            
           // 启动一个非后台线程,保持阻塞状态以确保Reactive Web Server 处于
           // 持续服务状态
			logger.info("Netty started on port(s): " + getPort());
			startDaemonAwaitThread(this.disposableServer);
		}
	}

   // 启动 Reactive Web 服务器,也就是 this.httpServer
   // 1. 关联 Http 请求处理器 handlerAdapter
   // 2. 服务端口绑定
	private DisposableServer startHttpServer() {
		if (this.lifecycleTimeout != null) {
           // 设置了超时参数时的启动 
			return this.httpServer.handle(this.handlerAdapter).bindNow(this.lifecycleTimeout);
		}
        
       // 未设置超时参数时的启动 (缺省情况) 
		return this.httpServer.handle(this.handlerAdapter).bindNow();
	}

	private ChannelBindException findBindException(Exception ex) {
		Throwable candidate = ex;
		while (candidate != null) {
			if (candidate instanceof ChannelBindException) {
				return (ChannelBindException) candidate;
			}
			candidate = candidate.getCause();
		}
		return null;
	}

	private void startDaemonAwaitThread(DisposableServer disposableServer) {
		Thread awaitThread = new Thread("server") {

			@Override
			public void run() {
              // 使 Reactive Web Server 处于无限阻塞状态
				disposableServer.onDispose().block();
			}

		};
		awaitThread.setContextClassLoader(getClass().getClassLoader());
       // 设置为前台线程并启动 
		awaitThread.setDaemon(false);
		awaitThread.start();
	}

	@Override
	public void stop() throws WebServerException {
		if (this.disposableServer != null) {
			if (this.lifecycleTimeout != null) {
				this.disposableServer.disposeNow(this.lifecycleTimeout);
			}
			else {
				this.disposableServer.disposeNow();
			}
			this.disposableServer = null;
		}
	}

	@Override
	public int getPort() {
		if (this.disposableServer != null) {
			return this.disposableServer.port();
		}
		return 0;
	}

}

从上面的代码可见,启动过程并不复杂,总结起来就是 :

  1. 启动Reactive Web服务器;

启动NettyWebServer所包装的Web Server,其实是一个HttpServer,主要是服务端端口绑定动作,并将其包装成一个DisposableServer;

  1. 启动一个前台线程阻塞等待Reactive Web服务器被释放;

该启动过程涉及到的主要是以Reactor Netty为中心的组件,看起来似乎跟Spring没有太多关系,但实际上,上述Netty Web服务器启动过程中一个重要参数ReactorHttpHandlerAdapter,代表了请求的处理逻辑,却正是来自Spring,这就是接下来我们要分析的ReactiveWebServerApplicationContext#getHttpHandler

2. ReactiveWebServerApplicationContext#getHttpHandler

   //  获取容器中有且仅有的一个类型为 HttpHandler 的 bean   ,
   // 如果容器中不存在该类型的 bean 或者有多个则抛出异常
	protected HttpHandler getHttpHandler() {
		// Use bean names so that we don't consider the hierarchy
       // 从容器获取所有类型为  HttpHandler 的 bean
		String[] beanNames = getBeanFactory().getBeanNamesForType(HttpHandler.class);
        
       // 如果容器中不存在类型为 HttpHandler 的 bean 则抛出异常 
		if (beanNames.length == 0) {
			throw new ApplicationContextException(
					"Unable to start ReactiveWebApplicationContext due to missing HttpHandler bean.");
		}
        
       // 如果容器中存在多个 HttpHandler bean 则抛出异常 
		if (beanNames.length > 1) {
			throw new ApplicationContextException(
					"Unable to start ReactiveWebApplicationContext due to multiple HttpHandler beans : "
							+ StringUtils.arrayToCommaDelimitedString(beanNames));
		}
        
       // 获取容器中有且仅有的一个类型为 HttpHandler 的 bean      
		return getBeanFactory().getBean(beanNames[0], HttpHandler.class);
	}

从方法getHttpHandler的逻辑来看,它要求容器中必须存在并且仅存在一个类型为HttpHandlerbean组件,如果容器中不存在该类型的bean 或者有多个则抛出异常。实际上,在本系列文章例子应用中,getHttpHandler方法会正常获取一个类型为HttpHandlerbean组件,bean名称为httpHandler,实现类为HttpWebHandlerAdapter

那么,这个bean httpHandler又来自哪里呢?这时候,我们要看Spring Boot的自动配置机制HttpHandlerAutoConfiguration了。

2.1 自动配置HttpHandlerAutoConfiguration

package org.springframework.boot.autoconfigure.web.reactive;

// 省略 import

@Configuration
@ConditionalOnClass({ DispatcherHandler.class, HttpHandler.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@ConditionalOnMissingBean(HttpHandler.class)
@AutoConfigureAfter({ WebFluxAutoConfiguration.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class HttpHandlerAutoConfiguration {

	@Configuration
	public static class AnnotationConfig {

		private ApplicationContext applicationContext;

		public AnnotationConfig(ApplicationContext applicationContext) {
			this.applicationContext = applicationContext;
		}

       // 定义 bean  httpHandler
		@Bean
		public HttpHandler httpHandler() {
			return WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
		}

	}

}

HttpHandlerAutoConfiguration是关于HttpHandler的自动配置类。从上面的代码不难看出,在Reactive Web应用环境中,并且容器中尚未定义HttpHandler bean的情况下,它会向容器定义一个httpHandler bean,具体的构建方法是使用WebHttpHandlerBuilder来构建,最终构建的HttpHandler实现类使用HttpWebHandlerAdapter

接下来,我们看看WebHttpHandlerBuilder构建HttpHandler的过程。

2.2 WebHttpHandlerBuilder

使用WebHttpHandlerBuilder构建HttpHandler的第一步,是基于应用上下文创建构建器对象自身:

	// WebHttpHandlerBuilder 代码片段
	public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
       // 获取容器中名称为 webHandler, 类型为 WebHandler 的 bean , 不能为 null
       // 基于此 bean 创建 builder 对象
		WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
				context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context);

       // 获取容器中所有类型为 WebFilter 的 bean,可以没有,可以多个
		List<WebFilter> webFilters = context
				.getBeanProvider(WebFilter.class)
				.orderedStream()
				.collect(Collectors.toList());
		builder.filters(filters -> filters.addAll(webFilters));
        
        // 获取容器中所有类型为 WebExceptionHandler 的 bean,可以没有,可以多个
		List<WebExceptionHandler> exceptionHandlers = context
				.getBeanProvider(WebExceptionHandler.class)
				.orderedStream()
				.collect(Collectors.toList());
		builder.exceptionHandlers(handlers -> handlers.addAll(exceptionHandlers));


       // 获取容器中名称为 webSessionManager, 类型为 WebSessionManager 的 bean , 可以为 null 
		try {
			builder.sessionManager(
					context.getBean(WEB_SESSION_MANAGER_BEAN_NAME, WebSessionManager.class));
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Fall back on default
		}

        // 获取容器中名称为 serverCodecConfigurer, 类型为 ServerCodecConfigurer 的 bean , 可以为 null  
		try {
			builder.codecConfigurer(
					context.getBean(SERVER_CODEC_CONFIGURER_BEAN_NAME, ServerCodecConfigurer.class));
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Fall back on default
		}

       // 获取容器中名称为 localeContextResolver, 类型为 LocaleContextResolver 的 bean , 可以为 null   
		try {
			builder.localeContextResolver(
					context.getBean(LOCALE_CONTEXT_RESOLVER_BEAN_NAME, LocaleContextResolver.class));
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Fall back on default
		}

       // 不可思议的事情发生了 , 这里为什么要重复上面的逻辑 ? 给我一个不是 bug 的理由 ?
		try {
			builder.localeContextResolver(
					context.getBean(LOCALE_CONTEXT_RESOLVER_BEAN_NAME, LocaleContextResolver.class));
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Fall back on default
		}

        // 获取容器中名称为 localeContextResolver, 类型为 ForwardedHeaderTransformer 的 bean , 可以为 null   
		try {
			builder.forwardedHeaderTransformer(
					context.getBean(FORWARDED_HEADER_TRANSFORMER_BEAN_NAME, ForwardedHeaderTransformer.class));
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Fall back on default
		}

		return builder;
	}

如上静态方法applicationContext其实是一个工厂方法,它从应用上下文中获取创建WebHttpHandlerBuilder所需要的各种工作组件bean,然后基于此创建了WebHttpHandlerBuilder对象。接下来才是使用WebHttpHandlerBuilder构建HttpHandler对象,如下所示 :

    /**
	 * Build the {@link HttpHandler}.
	 */
	public HttpHandler build() {

		WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);
		decorated = new ExceptionHandlingWebHandler(decorated,  this.exceptionHandlers);

       // 创建  HttpWebHandlerAdapter 对象
       // 一个 HttpWebHandlerAdapter 将一个 WebHandler 适配为一个 HttpHandler
		HttpWebHandlerAdapter adapted = new HttpWebHandlerAdapter(decorated);
		if (this.sessionManager != null) {
			adapted.setSessionManager(this.sessionManager);
		}
		if (this.codecConfigurer != null) {
			adapted.setCodecConfigurer(this.codecConfigurer);
		}
		if (this.localeContextResolver != null) {
			adapted.setLocaleContextResolver(this.localeContextResolver);
		}
		if (this.forwardedHeaderTransformer != null) {
			adapted.setForwardedHeaderTransformer(this.forwardedHeaderTransformer);
		}
		if (this.applicationContext != null) {
			adapted.setApplicationContext(this.applicationContext);
		}
		adapted.afterPropertiesSet();

		return adapted;
	}

到此为止,我们可以看到,Reactive Web服务器的启动主要就是以下三个要点 :

  1. 启动NettyWebServer,实际上是一个HttpServer;
  2. 启动一个前台线程阻塞等待NettyWebServer的结束;
  3. 所启动的NettyWebServer用于处理请求的HttpHandler来自自动配置HttpHandlerAutoConfiguration

Reactive Web服务器启动之后,应用就处于可以对外提供服务的状态了。不过,非常重要的一点,HttpHandler构建过程中从容器获取了各种这样的工作组件,那这些工作组件分别负责什么角色 ?又是来自哪里呢 ?

下一篇文章,我们来解答这些问题:自动配置机制WebFluxAutoConfiguration

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值