Spring的版本迭代很快,去年还是5.0,今年已经升级到5.1了。
别看只是小版本的升级,有些地方已经不兼容了。
比如,5.0时代,可以这样定制ConfigurableReactiveWebServerFactory:
@Bean
ConfigurableReactiveWebServerFactory webServerFactory() {
NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
NettyServerCustomizer customizer = options ->
options.eventLoopGroup(new NioEventLoopGroup(1)).option(CONNECT_TIMEOUT_MILLIS, 5000)
.afterNettyContextInit(context -> {
HttpServerRequest request = (HttpServerRequest) context;
if (URL_MQTT.equals(request.uri())) {
context.addHandlerFirst(IDLE_STATE_HANDLER,
new IdleStateHandler(5, 0, 0));
context.addHandlerLast("idleEventHandler", new IdleTimeoutHandler());
context.addHandlerLast("ws2bytebufDecoder", new WebSocketFrameToByteBufDecoder());
context.addHandlerLast("bytebuf2wsEncoder", new ByteBufToWebSocketFrameEncoder());
context.addHandlerLast("decoder", new MqttDecoder());
context.addHandlerLast("encoder", MqttEncoder.INSTANCE);
} else if (request.uri().endsWith("/websocket")) {
context.addHandlerFirst(IDLE_STATE_HANDLER,
new IdleStateHandler(5, 0, 0));
context.addHandlerLast("idleEventHandler", new IdleTimeoutHandler());
}
});
factory.addServerCustomizers(customizer);
return factory;
}
到了5.1时代,很多底层类发生了变化。
新写的一个项目是这样定制的:
@Component
public class ServerCustomizationBean extends ReactiveWebServerFactoryCustomizer {
public ServerCustomizationBean(ServerProperties serverProperties) {
super(serverProperties);
}
@Override
public void customize(ConfigurableReactiveWebServerFactory factory) {
super.customize(factory);
NettyReactiveWebServerFactory nettyFactory = (NettyReactiveWebServerFactory) factory;
nettyFactory.setResourceFactory(null);
nettyFactory.addServerCustomizers(server ->
server.tcpConfiguration(tcpServer ->
tcpServer.runOn(LoopResources.create("server", 1, DEFAULT_IO_WORKER_COUNT, true))
.selectorOption(CONNECT_TIMEOUT_MILLIS, 10000)
)
);
}
}
之所以要先nettyFactory.setResourceFactory(null),是因为本项目中,同时使用到了WebClient。在WebClient里,使用了自定义的ReactorResourceFactory。Spring在注入的时候,会自动注入到NettyReactiveWebServerFactory内。
查看NettyReactiveWebServerFactory类的源码:
if (this.resourceFactory != null) {
LoopResources resources = this.resourceFactory.getLoopResources();
Assert.notNull(resources,
"No LoopResources: is ReactorResourceFactory not initialized yet?");
server = server.tcpConfiguration((tcpServer) -> tcpServer.runOn(resources)
.addressSupplier(this::getListenAddress));
}
else {
server = server.tcpConfiguration(
(tcpServer) -> tcpServer.addressSupplier(this::getListenAddress));
}
可以得知,如果resourceFactory为null,就可以自定义LoopResources了。
自定义的LoopResources,selectCount为1,workerCount为:
/**
* Default worker thread count, fallback to available processor
* (but with a minimum value of 4)
*/
int DEFAULT_IO_WORKER_COUNT = Integer.parseInt(System.getProperty(
ReactorNetty.IO_WORKER_COUNT,
"" + Math.max(Runtime.getRuntime()
.availableProcessors(), 4)));
默认option见TcpServerBind类:
ServerBootstrap createServerBootstrap() {
return new ServerBootstrap().option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_BACKLOG, 1000)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_RCVBUF, 1024 * 1024)
.childOption(ChannelOption.SO_SNDBUF, 1024 * 1024)
.childOption(ChannelOption.AUTO_READ, false)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000)
.localAddress(new InetSocketAddress(DEFAULT_PORT));
}