Java ShutdownGracefully()永远不会返回
在这段代码中,shutdownGracefully().syncunterruptibly()永远不会返回。 这是预期的行为还是我误解了什么? 使用netty 4.0.35.Final进行测试Java ShutdownGracefully()永远不会返回,java,netty,Java,Netty,在这段代码中,shutdownGracefully().syncunterruptibly()永远不会返回。 这是预期的行为还是我误解了什么? 使用netty 4.0.35.Final进行测试 public class ShutdownGracefullyDemo { private ServerBootstrap bootstrap; public static void main(String[] args) throws Exception { new S
public class ShutdownGracefullyDemo {
private ServerBootstrap bootstrap;
public static void main(String[] args) throws Exception {
new ShutdownGracefullyDemo().initialize();
}
private void initialize() throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
bootstrap = new ServerBootstrap();
bootstrap.group(group)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new MyInboundHandler())
.bind(2025)
.sync()
.channel()
.closeFuture()
.sync();
}
@ChannelHandler.Sharable
private class MyInboundHandler extends SimpleChannelInboundHandler<ByteBuf> {
Charset charset = Charset.forName("US-ASCII");
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
throws Exception {
String data = msg.toString(charset);
System.out.println("Received: " + data);
if ("quit\r\n".equals(data)) {
shutdown();
}
}
}
private void shutdown() {
if (bootstrap != null && bootstrap.group() != null) {
bootstrap.group().shutdownGracefully().syncUninterruptibly();
System.out.println("shutdown completed");
}
}
}
公共类关闭GracefullyDemo{
私有服务器引导引导;
公共静态void main(字符串[]args)引发异常{
新建ShutdownGracefullyDemo().initialize();
}
private void initialize()引发InterruptedException{
NioEventLoopGroup组=新的NioEventLoopGroup();
bootstrap=newserverbootstrap();
bootstrap.group(组)
.channel(NioServerSocketChannel.class)
.handler(新的LoggingHandler(LogLevel.INFO))
.childHandler(新的MyInboundHandler())
.bind(2025年)
.sync()
.channel()
.closeFuture()
.sync();
}
@ChannelHandler.可共享
私有类MyInboundHandler扩展了SimpleChannelInboundHandler{
Charset Charset=Charset.forName(“US-ASCII”);
@凌驾
受保护的无效channelRead0(ChannelHandlerContext ctx,ByteBuf msg)
抛出异常{
字符串数据=msg.toString(字符集);
系统输出打印项次(“接收:+数据);
如果(“退出”\r\n“.equals(数据)){
关机();
}
}
}
私有无效关闭(){
if(bootstrap!=null&&bootstrap.group()!=null){
bootstrap.group();
系统输出打印项次(“关闭完成”);
}
}
}
您可以使用如下代码关闭:
public void shutdown() {
LOG.info("Gracefully shutdown http server ...");
bossGroup.shutdownGracefully(2, 2, TimeUnit.SECONDS);
workerGroup.shutdownGracefully(2, 2, TimeUnit.SECONDS);
}
类“io.netty.util.concurrent.DefaultPromise”上的方法“SyncUnterruptibly()”源代码:
@覆盖
公共承诺不断地等待着{
if(isDone()){
归还这个;
}
布尔值=假;
已同步(此){
而(!isDone()){
checkDeadLock();
incWaiters();
试一试{
等待();
}捕捉(中断异常e){
//在等待时被打断了。
中断=真;
}最后{
decWaiters();
}
}
}
如果(中断){
Thread.currentThread().interrupt();
}
归还这个;
}
任务在wait()方法上被阻止,它将永远不会继续,直到notify(),所以应用程序无法关闭完成。问题是您正在创建死锁 您正在从当前事件循环中的某个线程优雅地调用
shutdown
,这将导致该线程计划关闭,然后等待每个线程关闭后再继续。每个线程都不会关闭,因为您正在等待循环关闭,这实际上造成了死锁
在大型应用程序中,有多种方法可以解决此问题:
使用主线程:
这是通过拥有一个调用shutdown方法的全局应用程序主线程来解决的
将侦听器添加到正常关机:
而不是:
做
因为这允许它从shutdown方法中释放堆栈并正确关闭该线程感谢Derek的回答!在我的片段中,我从Norman Maurer的书《Netty In action》中获得了灵感,在关机示例中,方法
future.syncunterruptibly()
在ShutdownGracely()
之后调用,实际上调用关机并在操作完成时收到通知的underneth逻辑对我来说是有意义的。但由于一些让我困惑的原因,这并没有像预期的那样工作,因为没有人通知等待的线程。也许服务器正在等待所有客户端通道关闭,任务在这个通道上执行,就像死锁一样,所以你可以创建一个新的线程来关闭(或者其他更好的方式)。谢谢@Ferrybig,现在我得到了它。关于你的最后一个建议,关机不应该优雅地关闭所有频道吗?至少在阅读netty的文档时我了解到了这一点:“…它返回一个未来,当EventLoopGroup完全终止并且属于该组的所有通道都已关闭时通知您”@staedler您在最后一点上确实是正确的,netty通过关闭执行者的孩子来关闭频道
@Override
public Promise<V> awaitUninterruptibly() {
if (isDone()) {
return this;
}
boolean interrupted = false;
synchronized (this) {
while (!isDone()) {
checkDeadLock();
incWaiters();
try {
wait();
} catch (InterruptedException e) {
// Interrupted while waiting.
interrupted = true;
} finally {
decWaiters();
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
return this;
}
.shutdownGracefully().syncUninterruptibly();
System.out.println("shutdown completed");
shutdownGracefully().addListener(e -> {System.out.println("shutdown completed");})