[SpringCloud] Spring-Cloud-Gateway之启动过程(源码分析)
1 前言
1.1 环境信息
- Spring-Cloud-Gateway : 2.2.9.RELEASE
- org.springframework.boot:spring-boot:2.3.12.RELEASE
- io.projectreactor.netty:reactor-netty:0.9.20.RELEASE
- io.netty:netty-transport:4.1.65.FINAL
2 启动过程#与Netty的调用链路
2.1 简版(V1.0)
com.xxx.GatewayServiceBizApplication#main
org.springframework.boot.SpringApplication#run(java.lang.Class<?>[] primarySources, java.lang.String[] args)
org.springframework.boot.SpringApplication#run(java.lang.String...)
org.springframework.boot.SpringApplication#refreshContext
org.springframework.boot.SpringApplication#refresh(org.springframework.context.ApplicationContext)
org.springframework.boot.SpringApplication#refresh(org.springframework.context.ConfigurableApplicationContext)
org.springframework.context.ConfigurableApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#finishRefresh
org.springframework.context.LifecycleProcessor#onRefresh
org.springframework.context.support.DefaultLifecycleProcessor#onRefresh
org.springframework.context.support.DefaultLifecycleProcessor#startBeans
org.springframework.context.support.DefaultLifecycleProcessor.LifecycleGroup#start
org.springframework.context.support.DefaultLifecycleProcessor#doStart
org.springframework.boot.web.reactive.context.WebServerStartStopLifecycle#start
org.springframework.boot.web.reactive.context.WebServerManager#start
org.springframework.boot.web.server.WebServer#start
org.springframework.boot.web.embedded.netty.NettyWebServer#start
org.springframework.boot.web.embedded.netty.NettyWebServer#startHttpServer
reactor.netty.tcp.TcpServer#bind()
reactor.netty.tcp.TcpServer#configure = reactor.netty.tcp.TcpServerRunOn#configure(io.netty.bootstrap.ServerBootstrap, boolean, reactor.netty.resources.LoopResources)
reactor.netty.resources.DefaultLoopResources#cacheNioSelectLoops
2.2 详版(V2.0)
建议在
PC电脑端且选择下面源码区域在右上方的全屏按钮查看。否则,难以阅读。
com.xxx.GatewayServiceBizApplication#main
org.springframework.boot.SpringApplication#run(java.lang.Class<?>[] primarySources, java.lang.String[] args)
//public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// return new SpringApplication(primarySources).run(args); [#]
//}
org.springframework.boot.SpringApplication#run(java.lang.String...)
//public ConfigurableApplicationContext run(String... args) {
// StopWatch stopWatch = new StopWatch();
// stopWatch.start();
// ConfigurableApplicationContext context = null;
// configureHeadlessProperty();
// SpringApplicationRunListeners listeners = getRunListeners(args);
// listeners.starting();
// try {
// ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// configureIgnoreBeanInfo(environment);
// Banner printedBanner = printBanner(environment);
// context = createApplicationContext();
// prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 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, listeners);
// throw new IllegalStateException(ex);
// }
//
// try {
// listeners.running(context);
// }
// catch (Throwable ex) {
// handleRunFailure(context, ex, null);
// throw new IllegalStateException(ex);
// }
// return context;
//}
org.springframework.boot.SpringApplication#refreshContext
//private void refreshContext(ConfigurableApplicationContext context) {
// if (this.registerShutdownHook) {
// try {
// context.registerShutdownHook();
// }
// catch (AccessControlException ex) {
// // Not allowed in some environments.
// }
// }
// refresh((ApplicationContext) context); [#]
//}
org.springframework.boot.SpringApplication#refresh(org.springframework.context.ApplicationContext)
//protected void refresh(ApplicationContext applicationContext) {
// Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
// refresh((ConfigurableApplicationContext) applicationContext); [#]
//}
org.springframework.boot.SpringApplication#refresh(org.springframework.context.ConfigurableApplicationContext)
//protected void refresh(ConfigurableApplicationContext applicationContext) {
// applicationContext.refresh(); [#]
//}
org.springframework.context.ConfigurableApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#refresh
//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.
// 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();
// }
// }
//}
org.springframework.context.support.AbstractApplicationContext#finishRefresh
//protected void finishRefresh() {
// // Clear context-level resource caches (such as ASM metadata from scanning).
// clearResourceCaches();
//
// // Initialize lifecycle processor for this context.
// initLifecycleProcessor();
//
// // Propagate refresh to lifecycle processor first.
// getLifecycleProcessor().onRefresh(); [#]
//
// // Publish the final event.
// publishEvent(new ContextRefreshedEvent(this));
//
// // Participate in LiveBeansView MBean, if active.
// LiveBeansView.registerApplicationContext(this);
//}
org.springframework.context.LifecycleProcessor#onRefresh
org.springframework.context.support.DefaultLifecycleProcessor#onRefresh
//public void onRefresh() {
// startBeans(true); [#]
// this.running = true;
//}
org.springframework.context.support.DefaultLifecycleProcessor#startBeans
//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(); [#]
// }
// }
//}
org.springframework.context.support.DefaultLifecycleProcessor.LifecycleGroup#start
//public void start() {
// if (this.members.isEmpty()) {
// return;
// }
// if (logger.isDebugEnabled()) {
// logger.debug("Starting beans in phase " + this.phase);
// }
// Collections.sort(this.members);
// for (LifecycleGroupMember member : this.members) {
// doStart(this.lifecycleBeans, member.name, this.autoStartupOnly); [#]
// }
//}
org.springframework.context.support.DefaultLifecycleProcessor#doStart
//private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
// Lifecycle bean = lifecycleBeans.remove(beanName);
// if (bean != null && bean != this) {
// String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
// for (String dependency : dependenciesForBean) {
// doStart(lifecycleBeans, dependency, autoStartupOnly); [#] (递归调用)
// }
// if (!bean.isRunning() &&
// (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {
// if (logger.isTraceEnabled()) {
// logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]");
// }
// try {
// bean.start(); [#]
// }
// catch (Throwable ex) {
// throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex);
// }
// if (logger.isDebugEnabled()) {
// logger.debug("Successfully started bean '" + beanName + "'");
// }
// }
// }
//}
org.springframework.boot.web.reactive.context.WebServerStartStopLifecycle#start
//public void start() {
// this.weServerManager.start(); [#]
// this.running = true;
//}
org.springframework.boot.web.reactive.context.WebServerManager#start
//void start() {
// this.handler.initializeHandler();
// this.webServer.start(); [#]
// this.applicationContext
// .publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext));
//}
org.springframework.boot.web.server.WebServer#start {org.springframework.boot:spring-boot:2.3.12.RELEASE}
org.springframework.boot.web.embedded.netty.NettyWebServer#start {org.springframework.boot:spring-boot:2.3.12.RELEASE}
//public void start() throws WebServerException {
// if (this.disposableServer == null) {
// try {
// this.disposableServer = startHttpServer(); [#]
// }
// catch (Exception ex) {
// PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
// if (!isPermissionDenied(bindException.getCause())) {
// throw new PortInUseException(bindException.localPort(), ex);
// }
// });
// throw new WebServerException("Unable to start Netty", ex);
// }
// logger.info("Netty started on port(s): " + getPort());
// startDaemonAwaitThread(this.disposableServer);
// }
//}
org.springframework.boot.web.embedded.netty.NettyWebServer#startHttpServer
//private DisposableServer startHttpServer() {
// HttpServer server = this.httpServer;
// if (this.routeProviders.isEmpty()) {
// server = server.handle(this.handler);
// }
// else {
// server = server.route(this::applyRouteProviders);
// }
// if (this.lifecycleTimeout != null) {
// return server.bindNow(this.lifecycleTimeout);
// }
// return server.bindNow(); [#]
//}
reactor.netty.http.server.HttpServer#bindNow() {io.projectreactor.netty:reactor-netty:0.9.20.RELEASE}
//public final DisposableServer bindNow() {
// return bindNow(Duration.ofSeconds(45)); [#]
//}
reactor.netty.http.server.HttpServer#bindNow(java.time.Duration)
//public final DisposableServer bindNow(Duration timeout) {
// Objects.requireNonNull(timeout, "timeout");
// try {
// return Objects.requireNonNull(bind().block(timeout), "aborted"); [#]
// }
// catch (IllegalStateException e) {
// if (e.getMessage().contains("blocking read")) {
// throw new IllegalStateException("HttpServer couldn't be started within "
// + timeout.toMillis() + "ms");
// }
// throw e;
// }
//}
reactor.netty.http.server.HttpServer#bind() {io.projectreactor.netty:reactor-netty:0.9.20.RELEASE}
reactor.netty.tcp.TcpServer#bind() {io.projectreactor.netty:reactor-netty:0.9.20.RELEASE}
//static final TcpServer DEFAULT_TCP_SERVER = TcpServer.create();
//protected TcpServer tcpConfiguration() {
// return DEFAULT_TCP_SERVER;
//}
//public final Mono<? extends DisposableServer> bind() {
// return bind(tcpConfiguration()); [#]
//}
reactor.netty.http.server.HttpServerOperator#bind {io.projectreactor.netty:reactor-netty:0.9.20.RELEASE}
注: abstract class HttpServerOperator extends HttpServer
//protected Mono<? extends DisposableServer> bind(TcpServer b) {
// return source.bind(b); [#]
//}
reactor.netty.http.server.HttpServerBind#bind
//public Mono<? extends DisposableServer> bind(TcpServer delegate) {
// return delegate.bootstrap(this)
// .bind() [#]
// .map(CLEANUP_GLOBAL_RESOURCE);
//}
reactor.netty.tcp.TcpServer#bind
//public final Mono<? extends DisposableServer> bind() {
// ServerBootstrap b;
// try{
// b = configure(); [#]
// }
// catch (Throwable t){
// Exceptions.throwIfJvmFatal(t);
// return Mono.error(t);
// }
// return bind(b);
//}
reactor.netty.tcp.TcpServerBootstrap#configure
//public ServerBootstrap configure() {
// return Objects.requireNonNull(bootstrapMapper.apply(source.configure()), "bootstrapMapper"); [#]
//}
reactor.netty.tcp.TcpServerChannelGroup#configure
//public ServerBootstrap configure() {
// ServerBootstrap b = source.configure(); [#]
// b.attr(CHANNEL_GROUP, channelGroup);
// ConnectionObserver observer = BootstrapHandlers.childConnectionObserver(b);
// BootstrapHandlers.childConnectionObserver(b, observer.then(this));
// return b;
//}
reactor.netty.tcp.TcpServerRunOn#configure() {io.projectreactor.netty:reactor-netty:0.9.20.RELEASE}
//public ServerBootstrap configure() {
// ServerBootstrap b = source.configure();
//
// configure(b, preferNative, loopResources); [#]
//
// return b;
//}
reactor.netty.tcp.TcpServerRunOn#configure(io.netty.bootstrap.ServerBootstrap, boolean, reactor.netty.resources.LoopResources)
//static void configure(ServerBootstrap b,
// boolean preferNative,
// LoopResources resources) {
//
// EventLoopGroup selectorGroup = resources.onServerSelect(preferNative); [#]
// EventLoopGroup elg = resources.onServer(preferNative);
//
// b.group(selectorGroup, elg)
// .channel(resources.onServerChannel(elg));
//}
reactor.netty.tcp.TcpResources#onServerSelect
//public EventLoopGroup onServerSelect(boolean useNative) {
// return defaultLoops.onServerSelect(useNative); [#]
//}
reactor.netty.resources.DefaultLoopResources#onServerSelect
//public EventLoopGroup onServerSelect(boolean useNative) {
// if (useNative && preferNative()) {
// return cacheNativeSelectLoops();
// }
// return cacheNioSelectLoops(); [#]
//}
reactor.netty.resources.DefaultLoopResources#cacheNioSelectLoops
//EventLoopGroup cacheNioSelectLoops() {
// if (serverSelectLoops == serverLoops) {
// return cacheNioServerLoops();
// }
//
// EventLoopGroup eventLoopGroup = serverSelectLoops.get();
// if (null == eventLoopGroup) {
// EventLoopGroup newEventLoopGroup = new NioEventLoopGroup(selectCount, threadFactory(this, "select-nio")); [#]
// if (!serverSelectLoops.compareAndSet(null, newEventLoopGroup)) {
// //"FutureReturnValueIgnored" this is deliberate
// newEventLoopGroup.shutdownGracefully();
// }
// eventLoopGroup = cacheNioSelectLoops();
// }
// return eventLoopGroup;
//}
io.netty.channel.nio.NioEventLoopGroup#NioEventLoopGroup(int, java.util.concurrent.ThreadFactory) {io.netty:netty-transport:4.1.65.FINAL}
//public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
// this(nThreads, threadFactory, SelectorProvider.provider()); [#]1
//}
//public NioEventLoopGroup(
// int nThreads, ThreadFactory threadFactory, final SelectorProvider selectorProvider) {
// this(nThreads, threadFactory, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); [#]2
//}
//public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory,
// final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory) {
// super(nThreads, threadFactory, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()); [#]3
//}
io.netty.channel.MultithreadEventLoopGroup#MultithreadEventLoopGroup(int, java.util.concurrent.ThreadFactory, java.lang.Object...)
注: {io.netty:netty-transport:4.1.65.FINAL}
//private static final int DEFAULT_EVENT_LOOP_THREADS;
//static {
// DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
// "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
//
// if (logger.isDebugEnabled()) {
// logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
// }
//}
//protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
// super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args); [#]
//}
2.3 详版(V3.0)
2.3.1 阶段1:Sping-Cloud-Gateway 启动-创建httpserver [-6, 31]
# 运行环境 : windows x64 / 8 core cpu / 16 gb memory
# 运行场景/源码来源 : spring-cloud-gateway 启动过程 (by IDEA 断点)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory { org.springframework:spring-beans:5.2.15.RELEASE }
protected Object initializeBean(String beanName, Object bean, @Nullable org.springframework.beans.factory.support.RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean; // "org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration" / "org.springframework.cloud.gateway.config.GatewayAutoConfiguration$NettyConfiguration" /...
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); [#-6#] [#-4#] [#-2#] [#19#] [#22#] [#25#]
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean; // "NettyConfigurationXXXX" / "ServerProperties" / "NacosXXXX..."
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName); [#-5#] [#-3#] [#-1#] [#20#]
if (current == null) {
return result;
}
result = current;
}
return result;
}
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryConfiguration {spring-boot-autoconfigure:2.3.12.RELEASE}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(ReactiveWebServerFactory.class)
@ConditionalOnClass({ HttpServer.class })
static class EmbeddedNetty {
@Bean
@ConditionalOnMissingBean
ReactorResourceFactory reactorServerResourceFactory() {
return new ReactorResourceFactory(); [#1#]
}
@Bean
NettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory, // resourceFactory 即 [#1#] 的 reactorServerResourceFactory() Bean
ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers
) {
NettyReactiveWebServerFactory serverFactory = new NettyReactiveWebServerFactory();
serverFactory.setResourceFactory(resourceFactory); //[#15#]
routes.orderedStream().forEach(serverFactory::addRouteProviders); // [#16#]
serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return serverFactory;
}
}
@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
})
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration { spring-boot-autoconfigure:2.3.12.RELEASE }
@Bean
public ReactiveWebServerFactoryCustomizer reactiveWebServerFactoryCustomizer(ServerProperties serverProperties) {
return new ReactiveWebServerFactoryCustomizer(serverProperties); // [#17#] [#21#] [#23#]
}
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer { spring-boot-autoconfigure:2.3.12.RELEASE }
public ReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) { [#17#] [#24#]
this.serverProperties = serverProperties;
}
@Override
public void customize(ConfigurableReactiveWebServerFactory factory) { [#28#]
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
}
org.springframework.cloud.gateway.config.GatewayAutoConfiguration.NettyConfiguration { spring-cloud-gateway:2.2.9.RELEASE }
org.springframework.cloud.gateway.config.GatewayAutoConfiguration.NettyConfiguration
@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.httpserver.wiretap")
public NettyWebServerFactoryCustomizer nettyServerWiretapCustomizer(
Environment environment, ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties) {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(httpServer -> httpServer.wiretap(true)); [#18#]
super.customize(factory); [#30#]
}
};
}
org.springframework.boot.web.reactive.context.WebServerManager { org.framework:spring-web:2.3.RELEASE }
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); [#30#]
}
org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor
private void postProcessBeforeInitialization(org.springframework.boot.web.server.WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory)); [#26#]
}
com.xxx.gatewayservice.biz.nettyserver.config.NettyServerChannelOptionCustomizer
@Override
public void customize(ConfigurableReactiveWebServerFactory factory) {
super.customize(factory); [#27#]
logger.info("{} | start to set customization configs ...", BDP_GATEWAY_HTTP_SERVER_PREFIX);
//TODO
logger.info("{} | end to set customization configs ...", BDP_GATEWAY_HTTP_SERVER_PREFIX);
}
org.springframework.http.client.reactive.ReactorResourceFactory#afterPropertiesSet [#2#]
...
HttpResources httpResources = HttpResources.get(); [#3#]
...
reactor.netty.http.HttpResources#get [#4#] { io.projectreactor.netty:reactor-netty:0.9.20.RELEASE }
...
return getOrCreate(httpResources, null, null, ON_HTTP_NEW, "http"); [#5#]
...
reactor.netty.tcp.TcpResources#getOrCreate [#5#]
...
T resources = ref.get();
if (resources == null || loops != null || provider != null) {
update = create(resources, loops, provider, name, onNew); [#6#]
...
...
reactor.netty.tcp.TcpResources#create [#6#] { io.projectreactor.netty:reactor-netty:0.9.20.RELEASE }
if (previous == null) {
loops = loops == null ? LoopResources.create("reactor-" + name) : loops; [#7#] 注: loops == null : true
provider = provider == null ? // provider == null : true
ConnectionProvider.builder(name) [#10#]
.maxConnections(500) [#11#]
.pendingAcquireMaxCount(-1) [#12#]
.build() : provider; [#13#]
}
else {
loops = loops == null ? previous.defaultLoops : loops;
provider = provider == null ? previous.defaultProvider : provider;
}
return onNew.apply(loops, provider);
reactor.netty.resources.LoopResources#create(java.lang.String) [#7#] { io.projectreactor.netty:reactor-netty:0.9.20.RELEASE }
static LoopResources create(String prefix) { // prefix = "reactor-http"
return new DefaultLoopResources(
prefix
, DEFAULT_IO_SELECT_COUNT
, DEFAULT_IO_WORKER_COUNT
, true
);
}
int DEFAULT_IO_WORKER_COUNT = Integer.parseInt(System.getProperty( [#8#]
ReactorNetty.IO_WORKER_COUNT //reactor.netty.ReactorNetty # public static final String IO_WORKER_COUNT = "reactor.netty.ioWorkerCount"
, "" + Math.max(Runtime.getRuntime().availableProcessors(), 4) // 默认值 : max(4 , 可获得的cpu核数 )
)
);
reactor.netty.resources.DefaultLoopResources#DefaultLoopResources(java.lang.String, int, int, boolean) [#9#] { io.projectreactor.netty:reactor-netty:0.9.20.RELEASE }
DefaultLoopResources(
String prefix, // "reactor-http"
int selectCount, // -1
int workerCount, // 8
boolean daemon // true
) {
this.running = new AtomicBoolean(true); // true
this.daemon = daemon;
this.workerCount = workerCount;
this.prefix = prefix;
this.serverLoops = new AtomicReference<>();
this.clientLoops = new AtomicReference<>();
this.cacheNativeClientLoops = new AtomicReference<>();
this.cacheNativeServerLoops = new AtomicReference<>();
if (selectCount == -1) {
this.selectCount = workerCount; // 8
this.serverSelectLoops = this.serverLoops;
this.cacheNativeSelectLoops = this.cacheNativeServerLoops;
}
else {
this.selectCount = selectCount;
this.serverSelectLoops = new AtomicReference<>();
this.cacheNativeSelectLoops = new AtomicReference<>();
}
}
reactor.netty.resources.ConnectionProvider.ConnectionPoolSpec#maxConnections [#11#]
public final SPEC maxConnections(int maxConnections) {
if (maxConnections <= 0) {
throw new IllegalArgumentException("Max Connections value must be strictly positive");
}
this.maxConnections = maxConnections;
return get();
}
reactor.netty.resources.ConnectionProvider#builder [#10#]
long DEFAULT_POOL_MAX_IDLE_TIME = Long.parseLong(System.getProperty( [#10.4#]
ReactorNetty.POOL_MAX_IDLE_TIME, [#10.5#]
"-1")
);
static Builder builder(String name) { [#10.1#]
return new Builder(name); [#10.2#] name = http
}
private Builder(String name) { [#10.2#]
super(); [#10.3#] //此处断点结束后,即完成了 ConnectionPoolSpec 相关参数 的 初始化
name(name);
}
String DEFAULT_POOL_LEASING_STRATEGY = System.getProperty(
ReactorNetty.POOL_LEASING_STRATEGY // reactor.netty.ReactorNetty | public static final String POOL_LEASING_STRATEGY = "reactor.netty.pool.leasingStrategy";
, LEASING_STRATEGY_FIFO // reactor.netty.ReactorNetty | String LEASING_STRATEGY_FIFO = "fifo"; String LEASING_STRATEGY_LIFO = "lifo";
).toLowerCase(Locale.ENGLISH);
class ConnectionPoolSpec<SPEC extends ConnectionPoolSpec<SPEC>> implements Supplier<SPEC> {
int maxConnections = DEFAULT_POOL_MAX_CONNECTIONS; // 对象初始值(之后被覆盖为500) 16
int pendingAcquireMaxCount = PENDING_ACQUIRE_MAX_COUNT_NOT_SPECIFIED; // -2
Duration pendingAcquireTimeout = Duration.ofMillis(DEFAULT_POOL_ACQUIRE_TIMEOUT); // "PT45S"
Duration maxIdleTime; // null
Duration maxLifeTime; // null
String leasingStrategy = DEFAULT_POOL_LEASING_STRATEGY; // fifo
...
}
reactor.netty.resources.ConnectionProvider.ConnectionPoolSpec#ConnectionPoolSpec [#10.3#]
private ConnectionPoolSpec() { [#10.3#]
if (DEFAULT_POOL_MAX_IDLE_TIME > -1) { // DEFAULT_POOL_MAX_IDLE_TIME [#10.4#]
maxIdleTime(Duration.ofMillis(DEFAULT_POOL_MAX_IDLE_TIME));
}
if (DEFAULT_POOL_MAX_LIFE_TIME > -1) {
maxLifeTime(Duration.ofMillis(DEFAULT_POOL_MAX_LIFE_TIME));
}
}
reactor.netty.ReactorNetty#POOL_MAX_IDLE_TIME [#10.5#]
public static final String POOL_MAX_IDLE_TIME = "reactor.netty.pool.maxIdleTime";
org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
public void setResourceFactory(ReactorResourceFactory resourceFactory) { // [#14#]
this.resourceFactory = resourceFactory;
}
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
HttpServer httpServer = createHttpServer(); [#31#] // 至此, gateway.http-server 创建完成
ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
NettyWebServer webServer = new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout, getShutdown());
webServer.setRouteProviders(this.routeProviders);
return webServer;
}
断点至
#31#之后,日志的进度显示如下:


2.3.2 Spring-Cloud-Gateway : 创建 httpServer 至 创建 httpClient [0, 45]
org.springframework.cloud.gateway.config.GatewayAutoConfiguration
@Bean
@ConditionalOnEnabledPredicate
public ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory(
ServerCodecConfigurer codecConfigurer) {
return new ReadBodyRoutePredicateFactory(codecConfigurer.getReaders()); [#21#]
}
@Bean
@ConditionalOnEnabledPredicate
public RemoteAddrRoutePredicateFactory remoteAddrRoutePredicateFactory() {
return new RemoteAddrRoutePredicateFactory(); [#23#]
}
...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpClient.class)
protected static class NettyConfiguration {
@Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties(); [#0#]
}
@Bean
@ConditionalOnEnabledGlobalFilter
public NettyRoutingFilter routingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
HttpClientProperties properties) {
return new NettyRoutingFilter(httpClient, headersFilters, properties); [#15#] [#30#]
}
@Bean
@ConditionalOnMissingBean
public HttpClient gatewayHttpClient(HttpClientProperties properties,
List<HttpClientCustomizer> customizers) {
// configure pool resources
/**
* @self-comment
* properties = org.springframework.cloud.gateway.config.HttpClientProperties$$EnhancerBySpringCGLIB$$f0a036c4
* = 属性 : { connectTimeout / responseTimeout / maxHeaderSize / maxInitialLineLength / pool / proxy / ssl / websocket / wiretap / compression }
*/
ConnectionProvider connectionProvider = buildConnectionProvider(properties); [#2#] [#28#]
HttpClient httpClient = HttpClient.create(connectionProvider) [#11#]
// TODO: move customizations to HttpClientCustomizers
.httpResponseDecoder(spec -> {
if (properties.getMaxHeaderSize() != null) {
// cast to int is ok, since @Max is Integer.MAX_VALUE
spec.maxHeaderSize(
(int) properties.getMaxHeaderSize().toBytes());
}
if (properties.getMaxInitialLineLength() != null) {
// cast to int is ok, since @Max is Integer.MAX_VALUE
spec.maxInitialLineLength(
(int) properties.getMaxInitialLineLength().toBytes());
}
return spec;
}).tcpConfiguration(tcpClient -> {
if (properties.getConnectTimeout() != null) {
tcpClient = tcpClient.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
properties.getConnectTimeout());
}
// configure proxy if proxy host is set.
HttpClientProperties.Proxy proxy = properties.getProxy();
if (StringUtils.hasText(proxy.getHost())) {
tcpClient = tcpClient.proxy(proxySpec -> {
ProxyProvider.Builder builder = proxySpec
.type(ProxyProvider.Proxy.HTTP)
.host(proxy.getHost());
PropertyMapper map = PropertyMapper.get();
map.from(proxy::getPort).whenNonNull().to(builder::port);
map.from(proxy::getUsername).whenHasText()
.to(builder::username);
map.from(proxy::getPassword).whenHasText()
.to(password -> builder.password(s -> password));
map.from(proxy::getNonProxyHostsPattern).whenHasText()
.to(builder::nonProxyHosts);
});
}
return tcpClient;
});
HttpClientProperties.Ssl ssl = properties.getSsl();
if ((ssl.getKeyStore() != null && ssl.getKeyStore().length() > 0)
|| ssl.getTrustedX509CertificatesForTrustManager().length > 0
|| ssl.isUseInsecureTrustManager()) {
httpClient = httpClient.secure(sslContextSpec -> {
// configure ssl
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
X509Certificate[] trustedX509Certificates = ssl
.getTrustedX509CertificatesForTrustManager();
if (trustedX509Certificates.length > 0) {
sslContextBuilder = sslContextBuilder
.trustManager(trustedX509Certificates);
}
else if (ssl.isUseInsecureTrustManager()) {
sslContextBuilder = sslContextBuilder
.trustManager(InsecureTrustManagerFactory.INSTANCE);
}
try {
sslContextBuilder = sslContextBuilder
.keyManager(ssl.getKeyManagerFactory());
}
catch (Exception e) {
logger.error(e);
}
sslContextSpec.sslContext(sslContextBuilder)
.defaultConfiguration(ssl.getDefaultConfigurationType())
.handshakeTimeout(ssl.getHandshakeTimeout())
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
});
}
if (properties.isWiretap()) {
httpClient = httpClient.wiretap(true);
}
if (properties.isCompression()) {
httpClient = httpClient.compress(true);
}
if (!CollectionUtils.isEmpty(customizers)) {
customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
for (HttpClientCustomizer customizer : customizers) {
httpClient = customizer.customize(httpClient);
}
}
return httpClient;
}
...
private ConnectionProvider buildConnectionProvider( HttpClientProperties properties) {
HttpClientProperties.Pool pool = properties.getPool(); [#3#] [#29#]
ConnectionProvider connectionProvider;
if (pool.getType() == DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
}
else {
// create either Fixed or Elastic pool
ConnectionProvider.Builder builder = ConnectionProvider
.builder(pool.getName());
if (pool.getType() == FIXED) {
builder.maxConnections(pool.getMaxConnections())
.pendingAcquireMaxCount(-1).pendingAcquireTimeout(
Duration.ofMillis(pool.getAcquireTimeout()));
}
else {
// Elastic
builder.maxConnections(Integer.MAX_VALUE)
.pendingAcquireTimeout(Duration.ofMillis(0))
.pendingAcquireMaxCount(-1);
}
if (pool.getMaxIdleTime() != null) {
builder.maxIdleTime(pool.getMaxIdleTime());
}
if (pool.getMaxLifeTime() != null) {
builder.maxLifeTime(pool.getMaxLifeTime());
}
builder.evictInBackground(pool.getEvictionInterval());
connectionProvider = builder.build();
}
return connectionProvider;
}
}
org.springframework.http.codec.support.BaseCodecConfigurer#getReaders [#22#] [#27#]
@Override
public List<HttpMessageReader<?>> getReaders() { // [#22#]
this.defaultCodecs.applyDefaultConfig(this.customCodecs);
List<HttpMessageReader<?>> result = new ArrayList<>();
result.addAll(this.customCodecs.getTypedReaders().keySet());
result.addAll(this.defaultCodecs.getTypedReaders());
result.addAll(this.customCodecs.getObjectReaders().keySet());
result.addAll(this.defaultCodecs.getObjectReaders());
result.addAll(this.defaultCodecs.getCatchAllReaders());
return result;
}
com.alibaba.cloud.nacos.NacosDiscoveryProperties
public void setGroup(String group) {
this.group = group; [#24#]
}
org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory { spring-cloud-loadblancer:2.2.9.RELEASE }
public LoadBalancerClientFactory() {
super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME); [#25#]
}
org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration
@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
clientFactory.setConfigurations( this.configurations.getIfAvailable(Collections::emptyList) ); [#26#]
return clientFactory;
}
org.springframework.web.reactive.config.WebFluxConfigurationSupport
@Bean
public ResponseEntityResultHandler responseEntityResultHandler(
@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,
ServerCodecConfigurer serverCodecConfigurer,
@Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {
return new ResponseEntityResultHandler( [#31#]
serverCodecConfigurer.getWriters()
, contentTypeResolver
, reactiveAdapterRegistry
);
}
@Bean
public ServerResponseResultHandler serverResponseResultHandler(ServerCodecConfigurer serverCodecConfigurer) {
List<ViewResolver> resolvers = getViewResolverRegistry().getViewResolvers();
ServerResponseResultHandler handler = new ServerResponseResultHandler();
handler.setMessageWriters(serverCodecConfigurer.getWriters()); [#32#]
handler.setViewResolvers(resolvers);
return handler;
}
com.alibaba.cloud.nacos.registry.NacosServiceRegistry
public NacosServiceRegistry(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.nacosServiceManager = nacosServiceManager; [#33#]
}
org.springframework.context.support.AbstractApplicationContext
@Override
public void start() {
getLifecycleProcessor().start(); [#34#]
publishEvent(new ContextStartedEvent(this));
}
@Override
public void start() {
this.weServerManager.start(); [#35#]
this.running = true;
}
org.springframework.boot.web.reactive.context.WebServerManager
void start() { [#35#]
this.handler.initializeHandler();
this.webServer.start();
this.applicationContext
.publishEvent(new ReactiveWebServerInitializedEvent(this.webServer, this.applicationContext)); [#36#]
}
org.springframework.boot.web.embedded.netty.NettyWebServer
@Override
public void start() throws WebServerException {
if (this.disposableServer == null) {
try {
this.disposableServer = startHttpServer(); [#36#]
}
catch (Exception ex) {
PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
if (!isPermissionDenied(bindException.getCause())) {
throw new PortInUseException(bindException.localPort(), ex);
}
});
throw new WebServerException("Unable to start Netty", ex);
}
logger.info("Netty started on port(s): " + getPort());
startDaemonAwaitThread(this.disposableServer);
}
}
private DisposableServer startHttpServer() { [#37#]
HttpServer server = this.httpServer;
if (this.routeProviders.isEmpty()) {
server = server.handle(this.handler);
}
else {
server = server.route(this::applyRouteProviders);
}
if (this.lifecycleTimeout != null) {
return server.bindNow(this.lifecycleTimeout);
}
return server.bindNow();
}
reactor.netty.http.server.HttpServer
static final TcpServer DEFAULT_TCP_SERVER = TcpServer.create(); [#42#]
public final DisposableServer bindNow() {
return bindNow(Duration.ofSeconds(45)); [#38#]
}
public final DisposableServer bindNow(Duration timeout) {
Objects.requireNonNull(timeout, "timeout");
try {
return Objects.requireNonNull(bind().block(timeout), "aborted"); [#39#]
}
catch (IllegalStateException e) {
if (e.getMessage().contains("blocking read")) {
throw new IllegalStateException("HttpServer couldn't be started within "
+ timeout.toMillis() + "ms");
}
throw e;
}
}
public final Mono<? extends DisposableServer> bind() {
return bind(tcpConfiguration()); [#40#]
}
protected TcpServer tcpConfiguration() {
return DEFAULT_TCP_SERVER; [#41#]
}
reactor.netty.tcp.TcpServer { reactor-netty:0.9.20.RELEASE }
public static TcpServer create() {
return TcpServerBind.INSTANCE; [#43#]
}
reactor.netty.tcp.TcpServerBind
static final TcpServerBind INSTANCE = new TcpServerBind(); [#44#]
TcpServerBind() {
this.serverBootstrap = createServerBootstrap(); [#45#]
BootstrapHandlers.channelOperationFactory(this.serverBootstrap, TcpUtils.TCP_OPS);
}
ServerBootstrap createServerBootstrap() { [#45#]
return new ServerBootstrap()
.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.AUTO_READ, false)
.childOption(ChannelOption.TCP_NODELAY, true)
.localAddress(new InetSocketAddress(DEFAULT_PORT));
}
reactor.netty.http.server.HttpServerBind
public Mono<? extends DisposableServer> bind(TcpServer delegate) {
return delegate.bootstrap(this)
.bind()
.map(CLEANUP_GLOBAL_RESOURCE);
}
reactor.netty.tcp.TcpServer
public final Mono<? extends DisposableServer> bind() {
ServerBootstrap b;
try{
b = configure();
}
catch (Throwable t){
Exceptions.throwIfJvmFatal(t);
return Mono.error(t);
}
return bind(b);
}
@ConfigurationProperties("spring.cloud.gateway.httpclient")
@Validated
org.springframework.cloud.gateway.config.HttpClientProperties { spring-cloud-server:2.2.9.RELEASE }
private Pool pool = new Pool(); [#1#]
public static class Pool { [#1#]
/** Type of pool for HttpClient to use, defaults to ELASTIC. */
private PoolType type = PoolType.ELASTIC;
/** The channel pool map name, defaults to proxy. */
private String name = "proxy";
/**
* Only for type FIXED, the maximum number of connections before starting pending
* acquisition on existing ones.
*/
private Integer maxConnections = ConnectionProvider.DEFAULT_POOL_MAX_CONNECTIONS;
/**
* @slef-comment reactor.netty.resources.ConnectionProvider
* int DEFAULT_POOL_MAX_CONNECTIONS = Integer.parseInt(System.getProperty(ReactorNetty.POOL_MAX_CONNECTIONS, "" + Math.max(Runtime.getRuntime().availableProcessors(), 8) * 2));
*
*/
/**
* @slef-comment reactor.netty.resources.ConnectionProvider
* long DEFAULT_POOL_ACQUIRE_TIMEOUT = Long.parseLong(System.getProperty( ReactorNetty.POOL_ACQUIRE_TIMEOUT, "" + 45000));
*/
/** Only for type FIXED, the maximum time in millis to wait for aquiring. */
private Long acquireTimeout = ConnectionProvider.DEFAULT_POOL_ACQUIRE_TIMEOUT;
/**
* Time in millis after which the channel will be closed. If NULL, there is no max
* idle time.
*/
private Duration maxIdleTime = null;
/**
* Duration after which the channel will be closed. If NULL, there is no max life
* time.
*/
private Duration maxLifeTime = null;
/**
* Perform regular eviction checks in the background at a specified interval.
* Disabled by default ({@link Duration#ZERO})
*/
private Duration evictionInterval = Duration.ZERO;
...
public enum PoolType {
/**
* Elastic pool type.
*/
ELASTIC,
/**
* Fixed pool type.
*/
FIXED,
/**
* Disabled pool type.
*/
DISABLED
}
}
reactor.netty.resources.ConnectionProvider { reactor-netty:0.9.20.RELEASE }
long DEFAULT_POOL_MAX_IDLE_TIME = Long.parseLong(System.getProperty( ReactorNetty.POOL_MAX_IDLE_TIME, "-1")); [#4#] | reactor.netty.ReactorNetty : public static final String POOL_MAX_IDLE_TIME = "reactor.netty.pool.maxIdleTime";
long DEFAULT_POOL_MAX_LIFE_TIME = Long.parseLong(System.getProperty( ReactorNetty.POOL_MAX_LIFE_TIME, "-1")); [#5#] | reactor.netty.ReactorNetty : public static final String POOL_MAX_LIFE_TIME = "reactor.netty.pool.maxLifeTime";
static Builder builder(String name) { // name = "proxy"
return new Builder(name); [#3#]
}
final class Builder extends ConnectionPoolSpec<Builder> {
private ConnectionPoolSpec() {
if (DEFAULT_POOL_MAX_IDLE_TIME > -1) { [#5#] // DEFAULT_POOL_MAX_IDLE_TIME = -1
maxIdleTime(Duration.ofMillis(DEFAULT_POOL_MAX_IDLE_TIME));
}
if (DEFAULT_POOL_MAX_LIFE_TIME > -1) { [#6#] // DEFAULT_POOL_MAX_LIFE_TIME = -1
maxLifeTime(Duration.ofMillis(DEFAULT_POOL_MAX_LIFE_TIME));
}
}
private Builder(String name) { // name = "proxy"
super(); [#4#]
name(name);
}
public final SPEC maxConnections(int maxConnections) {
if (maxConnections <= 0) {
throw new IllegalArgumentException("Max Connections value must be strictly positive");
}
this.maxConnections = maxConnections; [#8#] maxConnections = Interger.MAX_VALUE = 2147483647
return get();
}
private ConnectionProvider buildConnectionProvider(
HttpClientProperties properties) {
HttpClientProperties.Pool pool = properties.getPool();
ConnectionProvider connectionProvider;
if (pool.getType() == DISABLED) {
connectionProvider = ConnectionProvider.newConnection();
}
else {
// create either Fixed or Elastic pool
ConnectionProvider.Builder builder = ConnectionProvider
.builder(pool.getName());
if (pool.getType() == FIXED) {
builder.maxConnections(pool.getMaxConnections())
.pendingAcquireMaxCount(-1).pendingAcquireTimeout(
Duration.ofMillis(pool.getAcquireTimeout()));
}
else {
// Elastic
builder.maxConnections(Integer.MAX_VALUE) [#7#] //允许的最大连接数
.pendingAcquireTimeout(Duration.ofMillis(0)) [#9#] //没有连接可用时,请求等待的最长时间
.pendingAcquireMaxCount(-1); [#10#] // 没有连接时,最多有多少个请求等待
}
if (pool.getMaxIdleTime() != null) {
builder.maxIdleTime(pool.getMaxIdleTime());
}
if (pool.getMaxLifeTime() != null) {
builder.maxLifeTime(pool.getMaxLifeTime());
}
builder.evictInBackground(pool.getEvictionInterval());
connectionProvider = builder.build();
}
return connectionProvider;
}
}
reactor.netty.tcp.TcpResources#create
static <T extends TcpResources> T create(@Nullable T previous,
@Nullable LoopResources loops, @Nullable ConnectionProvider provider,
String name,
BiFunction<LoopResources, ConnectionProvider, T> onNew) {
if (previous == null) {
loops = loops == null ? LoopResources.create("reactor-" + name) : loops;
provider = provider == null ?
ConnectionProvider
.builder(name)
.maxConnections(500)
.pendingAcquireMaxCount(-1) #
.build() : provider;
}
else {
loops = loops == null ? previous.defaultLoops : loops;
provider = provider == null ? previous.defaultProvider : provider;
}
return onNew.apply(loops, provider);
}
D:/Program_Data/maven_repository/io/netty/netty-common/4.1.65.Final/netty-common-4.1.65.Final-sources.jar!/io/netty/util/NetUtil.java:134 { netty-common:4.1.65.Final }
static {
logger.debug("-Djava.net.preferIPv4Stack: {}", IPV4_PREFERRED);
logger.debug("-Djava.net.preferIPv6Addresses: {}", IPV6_ADDRESSES_PREFERRED);
// Create IPv4 loopback address.
LOCALHOST4 = NetUtilInitializations.createLocalhost4();
// Create IPv6 loopback address.
LOCALHOST6 = NetUtilInitializations.createLocalhost6();
NetworkIfaceAndInetAddress loopback = NetUtilInitializations.determineLoopback(LOCALHOST4, LOCALHOST6);
LOOPBACK_IF = loopback.iface();
LOCALHOST = loopback.address();
// As a SecurityManager may prevent reading the somaxconn file we wrap this in a privileged block.
//
// See https://github.com/netty/netty/issues/3680
SOMAXCONN = AccessController.doPrivileged(new PrivilegedAction<Integer>() { #12#
@Override
public Integer run() {
// Determine the default somaxconn (server socket backlog) value of the platform.
// The known defaults:
// - Windows NT Server 4.0+: 200
// - Linux and Mac OS X: 128
int somaxconn = PlatformDependent.isWindows() ? 200 : 128; #13#
File file = new File("/proc/sys/net/core/somaxconn"); #14#
BufferedReader in = null;
try {
// file.exists() may throw a SecurityException if a SecurityManager is used, so execute it in the
// try / catch block.
// See https://github.com/netty/netty/issues/4936
if (file.exists()) {
in = new BufferedReader(new FileReader(file));
somaxconn = Integer.parseInt(in.readLine());
if (logger.isDebugEnabled()) {
logger.debug("{}: {}", file, somaxconn);
}
} else {
// Try to get from sysctl
Integer tmp = null;
if (SystemPropertyUtil.getBoolean("io.netty.net.somaxconn.trySysctl", false)) {
tmp = sysctlGetInt("kern.ipc.somaxconn");
if (tmp == null) {
tmp = sysctlGetInt("kern.ipc.soacceptqueue");
if (tmp != null) {
somaxconn = tmp;
}
} else {
somaxconn = tmp;
}
}
if (tmp == null) {
logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file,
somaxconn);
}
}
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}",
file, somaxconn, e);
}
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
// Ignored.
}
}
}
return somaxconn;
}
});
}
org.springframework.cloud.gateway.filter.NettyRoutingFilter { spring-cloud-gateway-server:2.2.9.RELEASE }
public NettyRoutingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider,
HttpClientProperties properties) {
this.httpClient = httpClient;
this.headersFiltersProvider = headersFiltersProvider;
this.properties = properties; [#16#]
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
applyBeanPostProcessorsBeforeInitialization
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
/**
* result :
* NettyRoutingFilter
* RemoveCacheBodyFilter
* ...
* ForwardRoutingFilter
* org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration
* org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration
* class org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties
* class org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration
* class org.springframework.boot.actuate.autoconfigure.metrics.JvmMetricsAutoConfiguration
* class io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
* class io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
* class io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics
*
*/
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName); [#17#]
if (current == null) {
return result;
}
result = current;
}
return result;
}
org.springframework.http.codec.support.BaseDefaultCodecs#
com.alibaba.cloud.nacos.NacosConfigManager#createConfigService [#18#]
org.springframework.http.codec.support.BaseDefaultCodecs { spring-web:5.2.15.RELEASE }
protected <T> void addCodec(List<T> codecs, T codec) {
initCodec(codec); [#19.afterStep#]
codecs.add(codec);
}
private void applyDefaultConfig(Map<?, Boolean> readers) {
readers.entrySet().stream()
.filter(Map.Entry::getValue)
.map(Map.Entry::getKey)
.forEach(this::initCodec); [#19.preStep#]
}
private void initCodec(@Nullable Object codec) { [#19#] [#20#] ...
if (codec instanceof DecoderHttpMessageReader) {
codec = ((DecoderHttpMessageReader) codec).getDecoder(); [#19.1#] ... //maxInMemorySize=262144 , decodableMimeTypes[0] = "*/*"
}
if (codec == null) {
return;
}
Integer size = this.maxInMemorySize; [#19.2#] [#20.2#] ...
if (size != null) {
if (codec instanceof AbstractDataBufferDecoder) {
((AbstractDataBufferDecoder<?>) codec).setMaxInMemorySize(size);
}
if (protobufPresent) {
if (codec instanceof ProtobufDecoder) {
((ProtobufDecoder) codec).setMaxMessageSize(size);
}
}
if (jackson2Present) {
if (codec instanceof AbstractJackson2Decoder) {
((AbstractJackson2Decoder) codec).setMaxInMemorySize(size);
}
}
if (jaxb2Present) {
if (codec instanceof Jaxb2XmlDecoder) {
((Jaxb2XmlDecoder) codec).setMaxInMemorySize(size);
}
}
if (codec instanceof FormHttpMessageReader) {
((FormHttpMessageReader) codec).setMaxInMemorySize(size);
}
if (codec instanceof ServerSentEventHttpMessageReader) {
((ServerSentEventHttpMessageReader) codec).setMaxInMemorySize(size);
initCodec(((ServerSentEventHttpMessageReader) codec).getDecoder());
}
if (synchronossMultipartPresent) {
if (codec instanceof SynchronossPartHttpMessageReader) {
((SynchronossPartHttpMessageReader) codec).setMaxInMemorySize(size);
}
}
}
Boolean enable = this.enableLoggingRequestDetails;
if (enable != null) {
if (codec instanceof FormHttpMessageReader) {
((FormHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
if (codec instanceof MultipartHttpMessageReader) {
((MultipartHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
if (synchronossMultipartPresent) {
if (codec instanceof SynchronossPartHttpMessageReader) {
((SynchronossPartHttpMessageReader) codec).setEnableLoggingRequestDetails(enable);
}
}
if (codec instanceof FormHttpMessageWriter) {
((FormHttpMessageWriter) codec).setEnableLoggingRequestDetails(enable);
}
if (codec instanceof MultipartHttpMessageWriter) {
((MultipartHttpMessageWriter) codec).setEnableLoggingRequestDetails(enable);
}
}
if (codec instanceof MultipartHttpMessageReader) {
initCodec(((MultipartHttpMessageReader) codec).getPartReader());
}
else if (codec instanceof MultipartHttpMessageWriter) {
initCodec(((MultipartHttpMessageWriter) codec).getFormWriter());
}
}
org.springframework.http.codec.support.ServerDefaultCodecsImpl#extendTypedReaders
3 核心模块源码
3.1 org.springframework.boot.web.embedded.netty.NettyWebServer
org.springframework.boot:spring-boot:2.3.12.RELEASE
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.embedded.netty;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.unix.Errors.NativeIoException;
import io.netty.util.concurrent.DefaultEventExecutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import reactor.netty.ChannelBindException;
import reactor.netty.DisposableServer;
import reactor.netty.http.server.HttpServer;
import reactor.netty.http.server.HttpServerRequest;
import reactor.netty.http.server.HttpServerResponse;
import reactor.netty.http.server.HttpServerRoutes;
import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult;
import org.springframework.boot.web.server.PortInUseException;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.server.WebServerException;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.util.Assert;
/**
* {@link WebServer} that can be used to control a Reactor Netty web server. Usually this
* class should be created using the {@link NettyReactiveWebServerFactory} and not
* directly.
*
* @author Brian Clozel
* @author Madhura Bhave
* @author Andy Wilkinson
* @since 2.0.0
*/
public class NettyWebServer implements WebServer {
/**
* Permission denied error code from {@code errno.h}.
*/
private static final int ERROR_NO_EACCES = -13;
private static final Predicate<HttpServerRequest> ALWAYS = (request) -> true;
private static final Log logger = LogFactory.getLog(NettyWebServer.class);
private final HttpServer httpServer;
private final BiFunction<? super HttpServerRequest, ? super HttpServerResponse, ? extends Publisher<Void>> handler;
private final Duration lifecycleTimeout;
private final GracefulShutdown gracefulShutdown;
private List<NettyRouteProvider> routeProviders = Collections.emptyList();
private volatile DisposableServer disposableServer;
public NettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter, Duration lifecycleTimeout,
Shutdown shutdown) {
Assert.notNull(httpServer, "HttpServer must not be null");
Assert.notNull(handlerAdapter, "HandlerAdapter must not be null");
this.lifecycleTimeout = lifecycleTimeout;
this.handler = handlerAdapter;
this.httpServer = httpServer.channelGroup(new DefaultChannelGroup(new DefaultEventExecutor()));
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(() -> this.disposableServer)
: null;
}
public void setRouteProviders(List<NettyRouteProvider> routeProviders) {
this.routeProviders = routeProviders;
}
@Override
public void start() throws WebServerException {
if (this.disposableServer == null) {
try {
this.disposableServer = startHttpServer();
}
catch (Exception ex) {
PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
if (!isPermissionDenied(bindException.getCause())) {
throw new PortInUseException(bindException.localPort(), ex);
}
});
throw new WebServerException("Unable to start Netty", ex);
}
logger.info("Netty started on port(s): " + getPort());
startDaemonAwaitThread(this.disposableServer);
}
}
private boolean isPermissionDenied(Throwable bindExceptionCause) {
try {
if (bindExceptionCause instanceof NativeIoException) {
return ((NativeIoException) bindExceptionCause).expectedErr() == ERROR_NO_EACCES;
}
}
catch (Throwable ex) {
}
return false;
}
@Override
public void shutDownGracefully(GracefulShutdownCallback callback) {
if (this.gracefulShutdown == null) {
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
return;
}
this.gracefulShutdown.shutDownGracefully(callback);
}
private DisposableServer startHttpServer() {
HttpServer server = this.httpServer;
if (this.routeProviders.isEmpty()) {
server = server.handle(this.handler);
}
else {
server = server.route(this::applyRouteProviders);
}
if (this.lifecycleTimeout != null) {
return server.bindNow(this.lifecycleTimeout);
}
return server.bindNow();
}
private void applyRouteProviders(HttpServerRoutes routes) {
for (NettyRouteProvider provider : this.routeProviders) {
routes = provider.apply(routes);
}
routes.route(ALWAYS, this.handler);
}
private void startDaemonAwaitThread(DisposableServer disposableServer) {
Thread awaitThread = new Thread("server") {
@Override
public void run() {
disposableServer.onDispose().block();
}
};
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
awaitThread.start();
}
@Override
public void stop() throws WebServerException {
if (this.disposableServer != null) {
if (this.gracefulShutdown != null) {
this.gracefulShutdown.abort();
}
try {
if (this.lifecycleTimeout != null) {
this.disposableServer.disposeNow(this.lifecycleTimeout);
}
else {
this.disposableServer.disposeNow();
}
}
catch (IllegalStateException ex) {
// Continue
}
this.disposableServer = null;
}
}
@Override
public int getPort() {
if (this.disposableServer != null) {
return this.disposableServer.port();
}
return -1;
}
}
3.2 reactor.netty.http.server.HttpServer
io.projectreactor.netty:reactor-netty:0.9.20.RELEASE
/*
* Copyright (c) 2011-Present VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.netty.http.server;
import java.net.SocketAddress;
import java.time.Duration;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.ConnectionObserver;
import reactor.netty.DisposableServer;
import reactor.netty.NettyPipeline;
import reactor.netty.channel.BootstrapHandlers;
import reactor.netty.http.HttpProtocol;
import reactor.netty.tcp.SslProvider;
import reactor.netty.tcp.TcpServer;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.Metrics;
/**
* An HttpServer allows to build in a safe immutable way an HTTP server that is
* materialized and connecting when {@link #bind(TcpServer)} is ultimately called.
* <p>Internally, materialization happens in two phases:</p>
* <ul>
* <li>first {@link #tcpConfiguration()} is called to retrieve a ready to use {@link TcpServer}</li>
* <li>then {@link #bind(TcpServer)} is called</li>
* </ul>
* <p>Examples:</p>
* <pre>
* {@code
* HttpServer.create()
* .host("0.0.0.0")
* .handle((req, res) -> res.sendString(Flux.just("hello")))
* .bind()
* .block();
* }
* </pre>
*
* @author Stephane Maldini
*/
public abstract class HttpServer {
/**
* Prepare an {@link HttpServer}
*
* @return a new {@link HttpServer}
*/
public static HttpServer create() {
return HttpServerBind.INSTANCE;
}
/**
* Prepare an {@link HttpServer}
*
* @return a new {@link HttpServer}
*/
public static HttpServer from(TcpServer tcpServer) {
return new HttpServerBind(tcpServer);
}
/**
* Bind the {@link HttpServer} and return a {@link Mono} of {@link DisposableServer}. If
* {@link Mono} is cancelled, the underlying binding will be aborted. Once the {@link
* DisposableServer} has been emitted and is not necessary anymore, disposing main server
* loop must be done by the user via {@link DisposableServer#dispose()}.
*
* If update configuration phase fails, a {@link Mono#error(Throwable)} will be returned
*
* @return a {@link Mono} of {@link DisposableServer}
*/
public final Mono<? extends DisposableServer> bind() {
return bind(tcpConfiguration());
}
/**
* Start the server in a blocking fashion, and wait for it to finish initializing
* or the startup timeout expires (the startup timeout is {@code 45} seconds). The
* returned {@link DisposableServer} offers simple server API, including to {@link
* DisposableServer#disposeNow()} shut it down in a blocking fashion.
*
* @return a {@link DisposableServer}
*/
public final DisposableServer bindNow() {
return bindNow(Duration.ofSeconds(45));
}
/**
* Start the server in a blocking fashion, and wait for it to finish initializing
* or the provided startup timeout expires. The returned {@link DisposableServer}
* offers simple server API, including to {@link DisposableServer#disposeNow()}
* shut it down in a blocking fashion.
*
* @param timeout max startup timeout
*
* @return a {@link DisposableServer}
*/
public final DisposableServer bindNow(Duration timeout) {
Objects.requireNonNull(timeout, "timeout");
try {
return Objects.requireNonNull(bind().block(timeout), "aborted");
}
catch (IllegalStateException e) {
if (e.getMessage().contains("blocking read")) {
throw new IllegalStateException("HttpServer couldn't be started within "
+ timeout.toMillis() + "ms");
}
throw e;
}
}
/**
* Start the server in a fully blocking fashion, not only waiting for it to initialize
* but also blocking during the full lifecycle of the server. Since most
* servers will be long-lived, this is more adapted to running a server out of a main
* method, only allowing shutdown of the servers through {@code sigkill}.
* <p>
* Note: {@link Runtime#addShutdownHook(Thread) JVM shutdown hook} is added by
* this method in order to properly disconnect the server upon receiving a
* {@code sigkill} signal.
* </p>
*
* @param timeout a timeout for server shutdown
* @param onStart an optional callback on server start
*/
public final void bindUntilJavaShutdown(Duration timeout,
@Nullable Consumer<DisposableServer> onStart) {
Objects.requireNonNull(timeout, "timeout");
DisposableServer facade = bindNow();
Objects.requireNonNull(facade, "facade");
if (onStart != null) {
onStart.accept(facade);
}
Runtime.getRuntime()
.addShutdownHook(new Thread(() -> facade.disposeNow(timeout)));
facade.onDispose()
.block();
}
/**
* Specifies whether GZip response compression/websocket compression
* extension is enabled if the client request
* presents accept encoding/websocket extensions headers.
* <p>
* Note: Using this method for enabling websocket compression is strongly discouraged.
* As of 0.9.5, use {@link WebsocketServerSpec#builder()} for providing websocket configuration.
* </p>
*
* @param compressionEnabled if true GZip response compression/websocket compression
* extension is enabled if the client request presents
* accept encoding/websocket extensions headers, otherwise disabled.
* @return a new {@link HttpServer}
*/
public final HttpServer compress(boolean compressionEnabled) {
if (compressionEnabled) {
return tcpConfiguration(COMPRESS_ATTR_CONFIG);
}
else {
return tcpConfiguration(COMPRESS_ATTR_DISABLE);
}
}
/**
* Enable GZip response compression if the client request presents accept encoding
* headers AND the response reaches a minimum threshold
*
* @param minResponseSize compression is performed once response size exceeds the given
* value in bytes
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer compress(int minResponseSize) {
if (minResponseSize < 0) {
throw new IllegalArgumentException("minResponseSize must be positive");
}
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.compressSize(b, minResponseSize)));
}
/**
* Enable GZip response compression if the client request presents accept encoding
* headers and the provided {@link java.util.function.Predicate} matches.
* <p>
* Note: the passed {@link HttpServerRequest} and {@link HttpServerResponse}
* should be considered read-only and the implement SHOULD NOT consume or
* write the request/response in this predicate.
* </p>
*
* @param predicate that returns true to compress the response.
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer compress(BiPredicate<HttpServerRequest, HttpServerResponse> predicate) {
Objects.requireNonNull(predicate, "compressionPredicate");
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.compressPredicate(b, predicate)));
}
/**
* Specifies whether support for the {@code "Forwarded"} and {@code "X-Forwarded-*"}
* HTTP request headers for deriving information about the connection is enabled.
*
* @param forwardedEnabled if true support for the {@code "Forwarded"} and {@code "X-Forwarded-*"}
* HTTP request headers for deriving information about the connection is enabled,
* otherwise disabled.
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer forwarded(boolean forwardedEnabled) {
if (forwardedEnabled) {
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.forwardedHeaderHandler(b, DefaultHttpForwardedHeaderHandler.INSTANCE)));
}
else {
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.forwardedHeaderHandler(b, null)));
}
}
/**
* Specifies a custom request handler for deriving information about the connection.
*
* @param handler the forwarded header handler
* @return a new {@link HttpServer}
* @since 0.9.12
*/
@SuppressWarnings("deprecation")
public final HttpServer forwarded(BiFunction<ConnectionInfo, HttpRequest, ConnectionInfo> handler) {
Objects.requireNonNull(handler, "handler");
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.forwardedHeaderHandler(b, handler)));
}
/**
* Specifies whether support for the {@code "HAProxy proxy protocol"}
* for deriving information about the address of the remote peer is enabled.
*
* @param proxyProtocolSupportType
* <ul>
* <li>
* choose {@link ProxyProtocolSupportType#ON}
* to enable support for the {@code "HAProxy proxy protocol"}
* for deriving information about the address of the remote peer.
* </li>
* <li>choose {@link ProxyProtocolSupportType#OFF} to disable the proxy protocol support.</li>
* <li>
* choose {@link ProxyProtocolSupportType#AUTO}
* then each connection of the same {@link HttpServer} will auto detect whether there is proxy protocol,
* so {@link HttpServer} can accept requests with or without proxy protocol at the same time.
* </li>
* </ul>
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer proxyProtocol(ProxyProtocolSupportType proxyProtocolSupportType) {
if (proxyProtocolSupportType == null) {
throw new NullPointerException("The parameter: proxyProtocolSupportType must not be null.");
}
if (proxyProtocolSupportType == ProxyProtocolSupportType.ON ||
proxyProtocolSupportType == ProxyProtocolSupportType.AUTO) {
if (!HAProxyMessageReader.hasProxyProtocol()) {
throw new UnsupportedOperationException(
"To enable proxyProtocol, you must add the dependency `io.netty:netty-codec-haproxy`" +
" to the class path first");
}
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> BootstrapHandlers.updateConfiguration(b,
NettyPipeline.ProxyProtocolDecoder,
(connectionObserver, channel) -> {
if (proxyProtocolSupportType == ProxyProtocolSupportType.ON) {
channel.pipeline()
.addFirst(NettyPipeline.ProxyProtocolDecoder, new HAProxyMessageDecoder())
.addAfter(NettyPipeline.ProxyProtocolDecoder,
NettyPipeline.ProxyProtocolReader, new HAProxyMessageReader());
}
else { // AUTO
channel.pipeline()
.addFirst(NettyPipeline.ProxyProtocolDecoder, new HAProxyMessageDetector());
}
})));
}
else if (proxyProtocolSupportType == ProxyProtocolSupportType.OFF) {
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> BootstrapHandlers.removeConfiguration(b, NettyPipeline.ProxyProtocolDecoder)));
}
else {
//Will never be here
throw new IllegalArgumentException("Invalid proxyProtocolSupportType");
}
}
/**
* The address to which this server should bind on subscribe.
*
* @param bindAddressSupplier A supplier of the address to bind to.
*
* @return a new {@link HttpServer}
* @since 0.9.7
*/
public final HttpServer bindAddress(Supplier<? extends SocketAddress> bindAddressSupplier) {
Objects.requireNonNull(bindAddressSupplier, "bindAddressSupplier");
return tcpConfiguration(tcpServer -> tcpServer.bindAddress(bindAddressSupplier));
}
/**
* Whether to enable metrics to be collected and registered in Micrometer's
* {@link io.micrometer.core.instrument.Metrics#globalRegistry globalRegistry}
* under the name {@link reactor.netty.Metrics#HTTP_SERVER_PREFIX}.
* <p><strong>Note:</strong>
* It is strongly recommended applications to configure an upper limit for the number of the URI tags.
* For example:
* <pre class="code">
* Metrics.globalRegistry
* .config()
* .meterFilter(MeterFilter.maximumAllowableTags(HTTP_SERVER_PREFIX, URI, 100, MeterFilter.deny()));
* </pre>
* <p>By default metrics are not enabled.
*
* @param metricsEnabled true enables metrics collection; false disables it
* @return a new {@link HttpServer}
* @deprecated as of 0.9.7. Use {@link #metrics(boolean, Function)}
*/
@Deprecated
public final HttpServer metrics(boolean metricsEnabled) {
return metrics(metricsEnabled, (Function<String, String>) null);
}
/**
* Whether to enable metrics to be collected and registered in Micrometer's
* {@link io.micrometer.core.instrument.Metrics#globalRegistry globalRegistry}
* under the name {@link reactor.netty.Metrics#HTTP_SERVER_PREFIX}.
* <p>{@code uriTagValue} function receives the actual uri and returns the uri tag value
* that will be used for the metrics with {@link reactor.netty.Metrics#URI} tag.
* For example instead of using the actual uri {@code "/users/1"} as uri tag value, templated uri
* {@code "/users/{id}"} can be used.
* <p><strong>Note:</strong>
* It is strongly recommended applications to configure an upper limit for the number of the URI tags.
* For example:
* <pre class="code">
* Metrics.globalRegistry
* .config()
* .meterFilter(MeterFilter.maximumAllowableTags(HTTP_SERVER_PREFIX, URI, 100, MeterFilter.deny()));
* </pre>
* <p>By default metrics are not enabled.
*
* @param metricsEnabled true enables metrics collection; false disables it
* @param uriTagValue a function that receives the actual uri and returns the uri tag value
* that will be used for the metrics with {@link reactor.netty.Metrics#URI} tag
* @return a new {@link HttpServer}
* @since 0.9.7
*/
@SuppressWarnings("deprecation")
public final HttpServer metrics(boolean metricsEnabled, @Nullable Function<String, String> uriTagValue) {
if (metricsEnabled) {
if (!Metrics.isInstrumentationAvailable()) {
throw new UnsupportedOperationException(
"To enable metrics, you must add the dependency `io.micrometer:micrometer-core`" +
" to the class path first");
}
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> {
BootstrapHandlers.updateMetricsSupport(b, MicrometerHttpServerMetricsRecorder.INSTANCE);
return HttpServerConfiguration.uriTagValue(b, uriTagValue);
}));
}
else {
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> {
BootstrapHandlers.removeMetricsSupport(b);
return HttpServerConfiguration.uriTagValue(b, null);
}));
}
}
/**
* Specifies whether the metrics are enabled on the {@link HttpServer}.
* All generated metrics are provided to the specified recorder.
*
* @param metricsEnabled if true enables the metrics on the server.
* @param recorder the {@link HttpServerMetricsRecorder}
* @return a new {@link HttpServer}
* @deprecated as of 0.9.7. Use {@link #metrics(boolean, Supplier)}
*/
@Deprecated
public final HttpServer metrics(boolean metricsEnabled, HttpServerMetricsRecorder recorder) {
return tcpConfiguration(tcpServer ->
tcpServer.metrics(metricsEnabled, recorder)
.bootstrap(b -> HttpServerConfiguration.uriTagValue(b, null)));
}
/**
* Specifies whether the metrics are enabled on the {@link HttpServer}.
* All generated metrics are provided to the specified recorder
* which is only instantiated if metrics are being enabled.
*
* @param metricsEnabled if true enables the metrics on the server.
* @param recorder a supplier for the {@link HttpServerMetricsRecorder}
* @return a new {@link HttpServer}
* @since 0.9.7
*/
@SuppressWarnings("deprecation")
public final HttpServer metrics(boolean metricsEnabled, Supplier<? extends HttpServerMetricsRecorder> recorder) {
return tcpConfiguration(tcpServer ->
tcpServer.metrics(metricsEnabled, recorder)
.bootstrap(b -> HttpServerConfiguration.uriTagValue(b, null)));
}
/**
* Specifies whether the metrics are enabled on the {@link HttpServer}.
* All generated metrics are provided to the specified recorder
* which is only instantiated if metrics are being enabled.
* <p>{@code uriValue} function receives the actual uri and returns the uri value
* that will be used when the metrics are propagated to the recorder.
* For example instead of using the actual uri {@code "/users/1"} as uri value, templated uri
* {@code "/users/{id}"} can be used.
*
* @param metricsEnabled if true enables the metrics on the server.
* @param recorder a supplier for the {@link HttpServerMetricsRecorder}
* @param uriValue a function that receives the actual uri and returns the uri value
* that will be used when the metrics are propagated to the recorder.
* @return a new {@link HttpServer}
* @since 0.9.12
*/
@SuppressWarnings("deprecation")
public final HttpServer metrics(boolean metricsEnabled, Supplier<? extends HttpServerMetricsRecorder> recorder,
Function<String, String> uriValue) {
return tcpConfiguration(tcpServer ->
tcpServer.metrics(metricsEnabled, recorder)
.bootstrap(b -> HttpServerConfiguration.uriTagValue(b, uriValue)));
}
/**
* The host to which this server should bind.
* By default the server will listen on any local address.
*
* @param host The host to bind to.
*
* @return a new {@link HttpServer}
*/
public final HttpServer host(String host) {
return tcpConfiguration(tcpServer -> tcpServer.host(host));
}
/**
* Attach an I/O handler to react on a connected client
*
* @param handler an I/O handler that can dispose underlying connection when {@link
* Publisher} terminates. Only the first registered handler will subscribe to the
* returned {@link Publisher} while other will immediately cancel given a same
* {@link Connection}
*
* @return a new {@link HttpServer}
*/
public final HttpServer handle(BiFunction<? super HttpServerRequest, ? super
HttpServerResponse, ? extends Publisher<Void>> handler) {
return new HttpServerHandle(this, handler);
}
/**
* Configure the {@link io.netty.handler.codec.http.HttpServerCodec}'s request decoding options.
*
* @param requestDecoderOptions a function to mutate the provided Http request decoder options
* @return a new {@link HttpServer}
*/
public final HttpServer httpRequestDecoder(Function<HttpRequestDecoderSpec, HttpRequestDecoderSpec> requestDecoderOptions) {
return tcpConfiguration(
requestDecoderOptions.apply(new HttpRequestDecoderSpec())
.build());
}
/**
* Specifies an idle timeout on the connection when it is waiting for an HTTP request (resolution: ms).
* Once the timeout is reached the connection will be closed.
* <p>If an {@code idleTimeout} is not specified, this indicates no timeout (i.e. infinite),
* which means the connection will be closed only if one of the peers decides to close it.
* <p>If the {@code idleTimeout} is less than {@code 1ms}, then {@code 1ms} will be the idle timeout.
* <p>By default {@code idleTimeout} is not specified.
*
* @param idleTimeout an idle timeout on the connection when it is waiting for an HTTP request (resolution: ms)
* @return a new {@link HttpServer}
* @since 0.9.15
*/
@SuppressWarnings("deprecation")
public final HttpServer idleTimeout(Duration idleTimeout) {
Objects.requireNonNull(idleTimeout, "idleTimeout");
return tcpConfiguration(tcp -> tcp.bootstrap(b -> HttpServerConfiguration.idleTimeout(b, idleTimeout)));
}
/**
* Configure the
* {@link ServerCookieEncoder}; {@link ServerCookieDecoder} will be
* chosen based on the encoder
*
* @param encoder the preferred ServerCookieEncoder
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer cookieCodec(ServerCookieEncoder encoder) {
ServerCookieDecoder decoder = encoder == ServerCookieEncoder.LAX ?
ServerCookieDecoder.LAX : ServerCookieDecoder.STRICT;
return tcpConfiguration(tcp -> tcp.bootstrap(
b -> HttpServerConfiguration.cookieCodec(b, encoder, decoder)));
}
/**
* Configure the
* {@link ServerCookieEncoder} and {@link ServerCookieDecoder}
*
* @param encoder the preferred ServerCookieEncoder
* @param decoder the preferred ServerCookieDecoder
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer cookieCodec(ServerCookieEncoder encoder, ServerCookieDecoder decoder) {
return tcpConfiguration(tcp -> tcp.bootstrap(
b -> HttpServerConfiguration.cookieCodec(b, encoder, decoder)));
}
/**
* Setup all lifecycle callbacks called on or after
* each child {@link io.netty.channel.Channel}
* has been connected and after it has been disconnected.
*
* @param observer a consumer observing state changes
*
* @return a new {@link HttpServer}
*/
public final HttpServer observe(ConnectionObserver observer) {
return new HttpServerObserve(this, observer);
}
/**
* The HTTP protocol to support. Default is {@link HttpProtocol#HTTP11}.
*
* @param supportedProtocols The various {@link HttpProtocol} this server will support
*
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer protocol(HttpProtocol... supportedProtocols) {
return tcpConfiguration(tcpServer -> tcpServer.bootstrap(b -> HttpServerConfiguration.protocols(b, supportedProtocols)));
}
/**
* The port to which this server should bind.
* By default the system will pick up an ephemeral port in the {@link #bind()} operation:
*
* @param port The port to bind to.
*
* @return a new {@link HttpServer}
*/
public final HttpServer port(int port) {
return tcpConfiguration(tcpServer -> tcpServer.port(port));
}
/**
* Apply an SSL configuration customization via the passed builder. The builder
* will produce the {@link SslContext} to be passed to with a default value of
* {@code 10} seconds handshake timeout unless the environment property {@code
* reactor.netty.tcp.sslHandshakeTimeout} is set.
*
* If {@link SelfSignedCertificate} needs to be used, the sample below can be
* used. Note that {@link SelfSignedCertificate} should not be used in production.
* <pre>
* {@code
* SelfSignedCertificate cert = new SelfSignedCertificate();
* SslContextBuilder sslContextBuilder =
* SslContextBuilder.forServer(cert.certificate(), cert.privateKey());
* secure(sslContextSpec -> sslContextSpec.sslContext(sslContextBuilder));
* }
* </pre>
*
* @param sslProviderBuilder builder callback for further customization of {@link SslContext}.
*
* @return a new {@link HttpServer}
*/
public final HttpServer secure(Consumer<? super SslProvider.SslContextSpec> sslProviderBuilder) {
return new HttpServerSecure(this, sslProviderBuilder);
}
/**
* Define routes for the server through the provided {@link HttpServerRoutes} builder.
*
* @param routesBuilder provides a route builder to be mutated in order to define routes.
* @return a new {@link HttpServer} starting the router on subscribe
*/
public final HttpServer route(Consumer<? super HttpServerRoutes> routesBuilder) {
Objects.requireNonNull(routesBuilder, "routeBuilder");
HttpServerRoutes routes = HttpServerRoutes.newRoutes();
routesBuilder.accept(routes);
return handle(routes);
}
/**
* Apply {@link ServerBootstrap} configuration given mapper taking currently
* configured one and returning a new one to be ultimately used for socket binding.
* <p>Configuration will apply during {@link #tcpConfiguration()} phase.</p>
*
* @param tcpMapper A {@link TcpServer} mapping function to update tcp configuration and
* return an enriched TCP server to use.
*
* @return a new {@link HttpServer}
*/
public final HttpServer tcpConfiguration(Function<? super TcpServer, ? extends TcpServer> tcpMapper) {
return new HttpServerTcpConfig(this, tcpMapper);
}
/**
* Provide a {@link ChannelGroup} to hold all active connected channels. Effectively,
* a shortcut for setting the same property on the underlying {@code TcpServer}:
* <pre>
* {@code
* HttpServer.create()
* .tcpConfiguration(server -> server.channelGroup(channelGroup))
* }
* </pre>
*
* <p><strong>Graceful Shutdown:</strong>
* <p>When a {@code ChannelGroup} is set, calls to {@link DisposableServer#disposeNow()}
* and {@link DisposableServer#disposeNow(Duration)} not only stop accepting new requests
* but also additionally wait for all active requests, in the {@code ChannelGroup}, to
* complete, within the given timeout.
*
* @param channelGroup a {@link ChannelGroup}
* @return a new {@link HttpServer}
* @since 0.9.6
* @see TcpServer#channelGroup(ChannelGroup)
*/
public final HttpServer channelGroup(ChannelGroup channelGroup) {
return tcpConfiguration(tcpServer -> tcpServer.channelGroup(channelGroup));
}
/**
* Apply or remove a wire logger configuration using {@link HttpServer} category
* and {@code DEBUG} logger level
*
* @param enable Specifies whether the wire logger configuration will be added to
* the pipeline
* @return a new {@link HttpServer}
*/
@SuppressWarnings("deprecation")
public final HttpServer wiretap(boolean enable) {
if (enable) {
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> BootstrapHandlers.updateLogSupport(b, LOGGING_HANDLER)));
}
else {
return tcpConfiguration(tcpServer ->
tcpServer.bootstrap(b -> BootstrapHandlers.removeConfiguration(b, NettyPipeline.LoggingHandler)));
}
}
/**
* Bind the {@link HttpServer} and return a {@link Mono} of {@link DisposableServer}
*
* @param b the {@link TcpServer} to bind
*
* @return a {@link Mono} of {@link DisposableServer}
*/
protected abstract Mono<? extends DisposableServer> bind(TcpServer b);
/**
* Materialize a {@link TcpServer} from the parent {@link HttpServer} chain to use with
* {@link #bind(TcpServer)} or separately
*
* @return a configured {@link TcpServer}
*/
protected TcpServer tcpConfiguration() {
return DEFAULT_TCP_SERVER;
}
static final TcpServer DEFAULT_TCP_SERVER = TcpServer.create();
static final LoggingHandler LOGGING_HANDLER = new LoggingHandler(HttpServer.class);
static final Logger log = Loggers.getLogger(HttpServer.class);
@SuppressWarnings("deprecation")
static final Function<TcpServer, TcpServer> COMPRESS_ATTR_CONFIG =
tcp -> tcp.bootstrap(HttpServerConfiguration.MAP_COMPRESS);
@SuppressWarnings("deprecation")
static final Function<TcpServer, TcpServer> COMPRESS_ATTR_DISABLE =
tcp -> tcp.bootstrap(HttpServerConfiguration.MAP_NO_COMPRESS);
}
Y 推荐资源
本文作者:
千千寰宇
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!

浙公网安备 33010602011771号