Netty 进阶之路笔记 (一)


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到指定端口后直接退出

案例二

当添加服务端channelcloseFuture之后仍然出现服务端退出现象

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,没有了用户进程,服务端就直接退出了。

避免方法

方案一

让主线程在服务端channelcloseFuture上进行阻塞直到服务端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)

文章作者: Ubi-potato
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ubi-potato !
评论
  目录