Netty服务端意外退出
案例一
服务端程序在bind()
方法阻塞执行完后直接退出
public static void main(string[] args){
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
try{
serverBootstrap
.channel(NioServerSocketChannel.class)
.group(bossGroup, workerGroup)
.childHandler(new RpcServerInitializer(60L, this.beanRegistry))
.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.bind(serverAddress, serverPort).sync();
}finally{
bossGroup.shutDownGracefully();
workerGroup.shutDownGracefully();
}
}
原因:在服务端进行绑定后,无论如何都将执行finally
代码块中的方法,将EventLoopGroup
关闭掉,此时主线程也执行完退出,EventLoopGroup
作为用户进程也被关闭,导致服务端bind
到指定端口后直接退出
案例二
当添加服务端channel
的closeFuture
之后仍然出现服务端退出现象
public static void main(string[] args){
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
try{
serverBootstrap
.channel(NioServerSocketChannel.class)
.group(bossGroup, workerGroup)
.childHandler(new RpcServerInitializer(60L, this.beanRegistry))
.option(ChannelOption.SO_BACKLOG, 128);
Channel channel = serverBootstrap.bind(serverAddress, serverPort).sync().channel();
channel.closeFuture().addListener(ChannelFutureListener.CLOSE);
}finally{
bossGroup.shutDownGracefully();
workerGroup.shutDownGracefully();
}
}
原因: channel.closeFuture().addListener(ChannelFutureListener.CLOSE)
执行过程完全是异步的,只是在channel
关闭时回掉ChannelFutureListener
进行相关操作,并不会因此阻塞在这行代码,所以相当于直接进入finally
代码块,关闭了NioEventLoopGroup
,没有了用户进程,服务端就直接退出了。
避免方法
方案一
让主线程在服务端channel
的closeFuture
上进行阻塞直到服务端channel
关闭
public static void main(string[] args){
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
try{
serverBootstrap
.channel(NioServerSocketChannel.class)
.group(bossGroup, workerGroup)
.childHandler(new RpcServerInitializer(60L, this.beanRegistry))
.option(ChannelOption.SO_BACKLOG, 128);
Channel channel = serverBootstrap.bind(serverAddress, serverPort).sync().channel();
channel.closeFuture().sync().addListener(ChannelFutureListener.CLOSE);
}finally{
bossGroup.shutDownGracefully();
workerGroup.shutDownGracefully();
}
}
方案二(推荐)
使用JDK的ShutdownHook
进行实现,删除掉finally块的NioEventLoopGroup
的关闭代码,将其移动到ShutdownHook
的关闭线程中。
public static void main(string[] args){
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
serverBootstrap
.channel(NioServerSocketChannel.class)
.group(bossGroup, workerGroup)
.childHandler(new RpcServerInitializer(60L, this.beanRegistry))
.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.bind(serverAddress, serverPort).sync();
Runtime.getRuntime().addShutdownHook(new Thread(()->{
bossGroup.shutDownGracefully();
workerGroup.shutDownGracefully();
}, "server-killer"));
}
方案三(推荐)
除了ShutdownHook
还有通过监听信号量注册signalHanlder
的方式进行优雅退出,但是需要判断不同操作系统的中断信号类型,如果是windows系统,则是SIGINT,否则选择SIGTERM信号,然后进行回掉注册:
Signal.handle(sig,shutdownHandler)