Netty启动之后马上退出问题排查
创始人
2024-03-21 08:10:01
0

在Java项目中main方法启动Netty项目之后,netty马上就退出了。这个问题一直困扰这我。最近终于吧问题理清楚了。下面是一些总结。

前提知识点

Java项目中JVM如果当前所有的线程都是守护线程的时候,会关闭服务器的。那么Netty主线程完成之后调用的是NioEventLoop线程,这个问题有可能会导致服务器关闭。

问题代码:

 EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();final EchoServerHandler serverHandler = new EchoServerHandler();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();p.addLast(new IdleStateHandler(10,10,10));p.addLast(new LoggingHandler(LogLevel.INFO));p.addLast(serverHandler);}});// Start the server.ChannelFuture f = b.bind(PORT).sync();} finally {// Shut down all event loops to terminate all threads.bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}

b.bind(PORT).sync()并没有将主线程给阻塞掉,因此最终还是会调用finally方法块,从而关闭EventLoop线程的导致。
监听端口的方法最终调用的是

 @Overrideprotected void doBind(SocketAddress localAddress) throws Exception {if (PlatformDependent.javaVersion() >= 7) {javaChannel().bind(localAddress, config.getBacklog());} else {javaChannel().socket().bind(localAddress, config.getBacklog());}}

这里的整个方法都是不会阻塞main方法的,如果没有其他方法来阻塞main方法,那么整个main方法都会执行,最终执行到finally方法块中将服务器关闭。 而且因为NioEventLoop线程并不是守护线程,因此也可以排查因为主线程退出之后,剩下的所有线程都是守护线程导致服务器退出的问题了。

解决方案

根据上面的逻辑我们知道有两种方法来解决这种问题。

  1. 调用某个方法将当前主线程阻塞掉
  2. 将主线程的finally代码块移动出Main线程中

解决方案1

在netty的启动的过程中有一个关闭channel的方法可以阻塞当前线程,并关服的时候会调用其operationComplete()方法。

具体代码如下

  // Start the server.ChannelFuture f = b.bind(PORT);// Wait until the server socket is closed.ChannelFuture sync = f.channel().closeFuture();sync.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture future) throws Exception {System.out.println("链路断开:"+future.channel().toString());}}).sync();

这里必须注意要加上.sync()同步方法,不然并不会阻塞main方法

解决方案2

将finally块中的代码丢到某个地方,我们直接用上面的方案1的即可当然此时我们需要将.sync()方法中去掉即可,因为不需要阻塞main方法。直接让main方法执行完毕退出即可。

    // Start the server.ChannelFuture f = b.bind(PORT);// Wait until the server socket is closed.ChannelFuture sync = f.channel().closeFuture();sync.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture future) throws Exception {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();System.out.println("链路断开:"+future.channel().toString());}})

当然我们也可以不丢在这里,我们自己写一个方法来调用优雅关服代码即可。当然,Netty的推荐方法还是用上面的两种方案来处理。

服务器如何主动关服

服务器关服无非就是要让channel调用close()方法。这里的close方法并不是ChannelHandler中的close。
ChannelHandler的事件回调方法中,ctx.close() 或 ctx.channel().close()只能关闭与某个客户端连接的channel
当时我们可以通过ChannelHandler事件回调中的链接的父亲来获取整个服务器的channel。

ctx.channel().parent()因此可以使用方法关闭服务器ctx.channel().parent().close();

还有一种方法来关闭服务器那就是在启动的时候保存一个静态变量来保存服务器的channel如下

 public static Channel serverChannel = null;...
...
...// Start the server.ChannelFuture f = b.bind(PORT).sync();//将ServerChannel保存起来方便后续直接调用关服serverChannel = f.channel();

在需要关服的地方直接嗲用serverChannel.close()关服即可。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
AWSECS:哪种网络模式具有... 使用AWS ECS中的awsvpc网络模式来获得最佳性能。awsvpc网络模式允许ECS任务直接在V...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...