Netty的closeFuture().sync().channel()会阻塞REST API。

8

我是在学习Netty,开始使用Spring Boot进行一些教程。我的目标是创建一个应用程序,设置一个TCP端口来接收消息,并通过REST API呈现它们。

大多数教程都说我应该添加类似于以下内容的东西

serverChannel = serverBootstrap.bind(tcpPort).sync().channel().closeFuture().sync().channel();

开始使用Netty。 当我这样做时,我实现的其余服务将不起作用。 现在,当我使用以下代码片段启动应用程序时:

serverChannel = serverBootstrap.bind(tcpPort).sync().channel();

一切似乎都正常工作。有人能解释一下可能会导致这个问题吗?

谢谢

2个回答

9

第一部分启动服务器,包括以下步骤: 1)绑定TCP端口, 2)等待服务器准备就绪(套接字正在监听), 3)返回相关联的通道。

serverBootstrap.bind(tcpPort).sync().channel();
                 (1)           (2)       (3)

第二部分是等待主通道(监听套接字)关闭 (closeFuture().sync()),其中closeFuture 为您提供了“关闭”操作(意味着服务器套接字的关闭)的“future”,而sync 等待此 future 完成。 channel()为您提供与第一次相同的通道,只不过现在已关闭。
因此,您可能会在各种示例中找到此代码,因为通常情况下,您在主线程中启动服务器(绑定),然后如果您不等待某些东西,主线程将结束,从而使您的 JVM 终止,因此您的服务器将立即停止启动。
因此,通常我们需要:
  • 启动服务器
  • 在管道中添加必要的处理程序来处理业务逻辑(当然还有网络协议)
  • 然后通过等待 closeFuture 来完成主线程,这样,一旦在业务逻辑中收到关闭命令,您就可以关闭主通道,从而关闭主线程。
例如,请参见 Shutdown netty programmatically

我自己的错误:(2) 等待主通道(监听套接字)准备就绪(未关闭),然后 (3) 返回相应的通道。而 channel().closeFuture().sync() closeFuture() 给出了此通道关闭的 ChannelFuture,然后 sync() 等待此通道关闭,因此关闭... - Frederic Brégier
为什么我们要关闭通道?这里的主通道是什么? - Zack
“main”通道是“通过套接字作为侦听器的通道”,但不是“真正的通道”,因为它没有连接(子通道将是已连接的通道,作为连接到侦听器服务的套接字,每个通道都有一个管道)。在关闭应用程序之前停止侦听地址/端口非常重要。因此,总结一下:“主通道”仅是根据绑定选项侦听地址/端口的侦听器,并且您必须等待其关闭(不再侦听,等待传入连接)才能关闭应用程序。 - Frederic Brégier

0

虽然有点老,但我也遇到了RestController无法启动的问题。其他答案帮助我解决了这个问题,但这里是Spring组件的完整代码。

import com.myserver.netty.handler.ClientInboundHandler;
import com.myserver.netty.handler.PacketDecoder;
import com.myserver.netty.handler.PacketEncoder;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@Log4j2
public class NettyServer {

   private EventLoopGroup masters = new NioEventLoopGroup();
   private EventLoopGroup workers = new NioEventLoopGroup();

   private Channel mainChannel;

   @PostConstruct
   public void start() {
       try {
           ServerBootstrap bootstrap = init();
           mainChannel = bootstrap.bind(8484).sync().channel(); // save the main channel so we can cleanly close it when app is shutdown
           log.info("Netty Server started......");
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

   @PreDestroy
   public void stop() throws InterruptedException {
       log.info("Shutting down netty server");
       workers.shutdownGracefully().sync();
       masters.shutdownGracefully().sync();
       mainChannel.closeFuture().sync();
       log.info("Shutdown complete");
   }

   private ServerBootstrap init() {
       return new ServerBootstrap()
               .group(masters, workers)
               .channel(NioServerSocketChannel.class)
               .option(ChannelOption.SO_BACKLOG, 5000)
               .option(ChannelOption.TCP_NODELAY, true)
               .option(ChannelOption.SO_KEEPALIVE, true)
               .childHandler(new ChannelInitializer<SocketChannel>() {
                   @Override
                   protected void initChannel(SocketChannel channel) throws Exception {
                       channel.pipeline()
                               .addLast(new PacketDecoder())
                               .addLast(new ClientInboundHandler())
                               .addLast(new PacketEncoder());
                   }
               });
   }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接