本文主要介绍 Spring 框架中 NettyWebServer
的启动运行机制。
源码分析基于:
在运行 Spring Boot Reactive 应用时,需要在应用内启动一个 WebServer
,那么 WebServer
的启动过程是怎样的呢?通过以下源码:
WebServerManager
WebServerManager(ReactiveWebServerApplicationContext applicationContext, ReactiveWebServerFactory factory,
Supplier<HttpHandler> handlerSupplier, boolean lazyInit) {
this.applicationContext = applicationContext;
Assert.notNull(factory, "Factory must not be null");
this.handler = new DelayedInitializationHttpHandler(handlerSupplier, lazyInit);
this.webServer = factory.getWebServer(this.handler);
}
可以看出:WebServer
是由 ReactiveWebServerFactory
在 WebServerManager
实例化时创建的。
那么 ReactiveWebServerFactory
是怎么创建的呢?通过以下源码:
ReactiveWebServerFactoryConfiguration.EmbeddedNetty
@Bean
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,
ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) {
NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
serverFactory.setResourceFactory(resourceFactory);
routes.orderedStream().forEach(serverFactory::addRouteProviders);
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return serverFactory;
}
可以看出:在 ReactiveWebServerFactoryConfiguration.EmbeddedNetty
中声明了 NettyReactiveWebServerFactory
。那么 ReactiveWebServerFactoryConfiguration.EmbeddedNetty
是如何启用的呢?通过以下源码:
ReactiveWebServerFactoryAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ReactiveHttpInputMessage.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })
public class ReactiveWebServerFactoryAutoConfiguration {...}
可以看出:在 ReactiveWebServerFactoryAutoConfiguration
中导入了 ReactiveWebServerFactoryConfiguration.EmbeddedNetty
。
那么 WebServerManager
是怎么创建的呢?通过以下源码:
ReactiveWebServerApplicationContext.createWebServer()
private void createWebServer() {
WebServerManager serverManager = this.serverManager;
if (serverManager == null) {
String webServerFactoryBeanName = getWebServerFactoryBeanName();
ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
this.serverManager = new WebServerManager(this, webServerFactory, this::getHttpHandler, lazyInit);
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.serverManager));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this.serverManager));
}
initPropertySources();
}
可以看出:WebServerManager
是在 ReactiveWebServerApplicationContext
中创建的。关于 ReactiveWebServerApplicationContext
的创建过程可以参考 SpringApplication启动时如何选择ConfigurableApplicationContext。
小结:
ReactiveWebServerApplicationContext.createWebServer()
new WebServerManager(…,ReactiveWebServerFactory,…)
ReactiveWebServerFactory.getWebServer(HttpHandler)
已经创建好了 WebServer
,那么何时启动呢?通过以下源码:
WebServerStartStopLifecycle.start()
public void start() {
this.weServerManager.start();
this.running = true;
}
可以看出:在 WebServerStartStopLifecycle.start()
中调用了 WebServerManager.start()
,而 WebServerManager.start()
又调用了 WebServer.start()
。那么 WebServerStartStopLifecycle.start()
是何时被调用的呢?在 ReactiveWebServerApplicationContext.createWebServer
中,已经将 WebServerStartStopLifecycle
注册到了应用上下文,WebServerStartStopLifecycle
实现了 Lifecycle
接口。通过以下源码:
DefaultLifecycleProcessor.startBeans(boolean)
private void startBeans(boolean autoStartupOnly) {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
phases.get(key).start();
}
}
}
可以看出:Lifecycle.start()
会被 DefaultLifecycleProcessor.startBeans(boolean)
调用。从应用启动到 WebServer.start()
,完整的调用路径如下:
public void onRefresh() {
startBeans(true);
this.running = true;
}
小结:
ReactiveWebServerApplicationContext.createWebServer()
:注册 WebServerStartStopLifecycle
DefaultLifecycleProcessor.onRefresh()
:触发 WebServerStartStopLifecycle.start()
现在服务已经启动了,那么服务启动之后,如何运行我们的代码呢?通过以下源码:
NettyReactiveWebServerFactory.getWebServer
public WebServer getWebServer(HttpHandler httpHandler) {
HttpServer httpServer = createHttpServer();
ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
NettyWebServer webServer = new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout, getShutdown());
webServer.setRouteProviders(this.routeProviders);
return webServer;
}
可以看出:构造 NettyWebServer
需要传入 HttpHandler
,而这个 HttpHandler
就是 NettyWebServer
处理请求的主要入口。那么 HttpHandler
是怎么被创建的呢?通过以下源码:
HttpHandlerAutoConfiguration.AnnotationConfig
@Bean
public HttpHandler httpHandler(ObjectProvider<WebFluxProperties> propsProvider) {
HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(this.applicationContext).build();
WebFluxProperties properties = propsProvider.getIfAvailable();
if (properties != null && StringUtils.hasText(properties.getBasePath())) {
Map<String, HttpHandler> handlersMap = Collections.singletonMap(properties.getBasePath(), httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
return httpHandler;
}
可以看出:HttpHandler
是由 WebHttpHandlerBuilder.applicationContext(ApplicationContext).build()
构建的。通过以下源码:
WebHttpHandlerBuilder.applicationContext
public static WebHttpHandlerBuilder applicationContext(ApplicationContext context) {
WebHttpHandlerBuilder builder = new WebHttpHandlerBuilder(
context.getBean(WEB_HANDLER_BEAN_NAME, WebHandler.class), context);
List<WebFilter> webFilters = context
.getBeanProvider(WebFilter.class)
.orderedStream()
.collect(Collectors.toList());
builder.filters(filters -> filters.addAll(webFilters));
...
return builder;
}
可以看出: WebHttpHandlerBuilder
从 ApplicationContext
中取出以下 Bean:
WebHandler
WebFilter
并排序WebExceptionHandler
并排序WebSessionManager
ServerCodecConfigurer
LocaleContextResolver
并使用这些 Bean 构建 HttpHandler
:
WebHttpHandlerBuilder.build
public HttpHandler build() {
WebHandler decorated = new FilteringWebHandler(this.webHandler, this.filters);
decorated = new ExceptionHandlingWebHandler(decorated, this.exceptionHandlers);
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;
}
最终构建出 HttpHandler
类似以下示例:
HttpHandler httpHandler = new HttpWebHandlerAdapter(
new ExceptionHandlingWebHandler(
new FilteringWebHandler(
new DispatcherHandler(...)
, this.filters)
, this.exceptionHandlers)
);
小结:
NettyReactiveWebServerFactory.getWebServer(HttpHandler)
WebHttpHandlerBuilder.applicationContext(ApplicationContext).build()
查看源码过程中,发现一个 BUG,已上报到 spring-framework。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。