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()关服即可。

相关内容

热门资讯

保存时出现了1个错误,导致这篇... 当保存文章时出现错误时,可以通过以下步骤解决问题:查看错误信息:查看错误提示信息可以帮助我们了解具体...
汇川伺服电机位置控制模式参数配... 1. 基本控制参数设置 1)设置位置控制模式   2)绝对值位置线性模...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
表格中数据未显示 当表格中的数据未显示时,可能是由于以下几个原因导致的:HTML代码问题:检查表格的HTML代码是否正...
本地主机上的图像未显示 问题描述:在本地主机上显示图像时,图像未能正常显示。解决方法:以下是一些可能的解决方法,具体取决于问...
表格列调整大小出现问题 问题描述:表格列调整大小出现问题,无法正常调整列宽。解决方法:检查表格的布局方式是否正确。确保表格使...
不一致的条件格式 要解决不一致的条件格式问题,可以按照以下步骤进行:确定条件格式的规则:首先,需要明确条件格式的规则是...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...