复制下载ZY -》:https://www.97yrbl.com/t-1387.html
咱们知道,Spring-Cloud-Gateway 其实底层也是依据 Spring Boot 的。首要来看下 Spring Boot 中初始化哪种 web 容器的选择原理:首要第一步是依据类是否存在确定是哪种 WebApplicationType:
WebApplicationType
public enum WebApplicationType {
/**
* 没有 web 服务,不需要 web 容器
*/
NONE,
/**
* 运用依据 servlet 的 web 容器
*/
SERVLET,
/**
* 运用照应式的 web 容器
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
拷贝代码 拷贝代码
复制代码
复制代码
从源码中能够看出,当有 WEBFLUX_INDICATOR_CLASS 而且没有 WEBMVC_INDICATOR_CLASS 以及 JERSEY_INDICATOR_CLASS 的时分,判别为 REACTIVE 环境。假定全部 SERVLET_INDICATOR_CLASSES 就以为是 SERVLET 环境。其实这样也能够看出,假定又引入 spring-web 又引入 spring-webflux 的依靠,其实仍是 SERVLET 环境。假定以上都没有,那么就是无 web 容器的环境。在 Spring-Cloud-Gateway 中,是 REACTIVE 环境。
假定是 REACTIVE 环境,就会运用org.springframework.boot.web.reactive.server.ReactiveWebServerFactory的完成 Bean 创立 web 容器。那么究竟是哪个完成呢?现在有四个完成(Spring-boot 2.7.x):
- TomcatReactiveWebServerFactory:依据 Tomcat 的照应式 web 容器 Factory
- JettyReactiveWebServerFactory:依据 Jetty 的照应式 web 容器 Factory
- UndertowReactiveWebServerFactory:依据 Undertow 的照应式 web 容器 Factory
- NettyReactiveWebServerFactory:依据 Netty 的照应式 web 容器 Factory
实践会用哪个,看究竟哪个 Bean 会注册到 ApplicationContext 中:
ReactiveWebServerFactoryConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {
@Bean
@ConditionalOnMissingBean
ReactorResourceFactory reactorServerResourceFactory() {
return new ReactorResourceFactory();
}
@Bean
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,
ObjectProviderroutes, ObjectProviderserverCustomizers) {
NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
serverFactory.setResourceFactory(resourceFactory);
routes.orderedStream().forEach(serverFactory::addRouteProviders);
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return serverFactory;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ org.apache.catalina.startup.Tomcat.class })
static class EmbeddedTomcat {
@Bean
TomcatReactiveWebServerFactory tomcatReactiveWebServerFactory(
ObjectProviderconnectorCustomizers,
ObjectProvidercontextCustomizers,
ObjectProvider> protocolHandlerCustomizers) {
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ org.eclipse.jetty.server.Server.class, ServletHolder.class })
static class EmbeddedJetty {
@Bean
@ConditionalOnMissingBean
JettyResourceFactory jettyServerResourceFactory() {
return new JettyResourceFactory();
}
@Bean
JettyReactiveWebServerFactory jettyReactiveWebServerFactory(JettyResourceFactory resourceFactory,
ObjectProviderserverCustomizers) {
JettyReactiveWebServerFactory serverFactory = new JettyReactiveWebServerFactory();
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
serverFactory.setResourceFactory(resourceFactory);
return serverFactory;
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ Undertow.class })
static class EmbeddedUndertow {
@Bean
UndertowReactiveWebServerFactory undertowReactiveWebServerFactory(
ObjectProviderbuilderCustomizers) {
UndertowReactiveWebServerFactory factory = new UndertowReactiveWebServerFactory();
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
拷贝代码 拷贝代码
复制代码
复制代码
从原码能够看出,每种配备上都有@ConditionalOnMissingBean(ReactiveWebServerFactory.class)以及判别是否有对应容器的 class 的条件,例如:@ConditionalOnClass({ Undertow.class }),@Configuration(proxyBeanMethods = false)是封闭这个配备中 Bean 之间的署理加快加载速度。
由于每个配备都有@ConditionalOnMissingBean(ReactiveWebServerFactory.class),那么其实能保证就算满意多个配备的条件,最终也只需一个 ReactiveWebServerFactory,那么当满意多个条件时,哪个优先加载呢?这就要看这里的源码:
ReactiveWebServerFactoryAutoConfiguration
@Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class,
ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class,
ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class,
ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class })
拷贝代码 拷贝代码
复制代码
复制代码
从这里能够看出,是依照EmbeddedTomcat,EmbeddedJetty,EmbeddedUndertow,EmbeddedNetty的顺序 Import 的,也就是:只需你的依靠中加入了任何 Web 容器(例如 Undertow),那么最终创立的就是依据那个 web 容器的异步容器,而不是依据 netty 的
为何 Web 容器换了就会有问题
首要, Spring Cloud Gateway
Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux. It does not work in a traditional Servlet Container or when built as a WAR.
就是 Spring Cloud Gateway 只能在 Netty 的环境中运转。这是为什么呢。当初在规划的时分,就假定了容器只能是 Netty,后续开发各种 Spring Cloud Gateway 的内置 Filter 以及 Filter 插件的时分,有许多假定其时就是 Netty 的代码,例如缓存 Body 的 Filter 运用的东西类ServerWebExchangeUtils:
private staticMonocacheRequestBody(ServerWebExchange exchange, boolean cacheDecoratedRequest,
Function function) {
ServerHttpResponse response = exchange.getResponse();
//在这里,强制转换了 bufferFactory 为 NettyDataBufferFactory
NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();
// Join all the DataBuffers so we have a single DataBuffer for the body
return DataBufferUtils.join(exchange.getRequest().getBody())
.defaultIfEmpty(factory.wrap(new EmptyByteBuf(factory.getByteBufAllocator())))
.map(dataBuffer -> decorate(exchange, dataBuffer, cacheDecoratedRequest))
.switchIfEmpty(Mono.just(exchange.getRequest())).flatMap(function);
}
拷贝代码 拷贝代码
复制代码
复制代码
从源码中能够看到,代码直接以为 response 中的 BufferFactory 就是 NettyDataBufferFactory,其实在其他 Web 容器的情况下,现在应该是 DefaultDataBufferFactory,这样就会有异常。不过在 v3.0.5 之后的版别,现已修复了这个强转