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)
