[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 推荐资源

posted @ 2023-05-12 11:35  千千寰宇  阅读(802)  评论(0)    收藏  举报