Netty systemd 惰性初始化

Netty systemd lazy initialization

是否可以使用继承的服务器套接字通道通过 systemd/inetd 初始化 netty 惰性? 我们在旧的基于 Jetty 的服务器中使用了它,Jetty 会调用 Java 的 System.inheritedChannel() 来获取通过 systemd 在延迟初始化时创建的套接字。

我搜索了很多,我只找到了一张 Jira 票据,上面说它应该在版本 4 中支持:https://issues.jboss.org/browse/NETTY-309

但是这个 jira 票没有例子,我找不到任何文档,也找不到源代码上的任何内容,可以指出如何在 netty 中实现这一点。

如有任何帮助,我们将不胜感激。
谢谢

编辑: 只是为了更清楚,我想知道是否可以让我的 java 应用程序套接字由 systemd 激活,然后以某种方式将套接字引用传递给 netty。

编辑 2: 这是 Norman Mayer 建议的一种方法,但它实际上失败了,但有以下例外:

public class MyServerBootStrap {

  private ServiceContext ctx;
  private Config config;
  private Collection<Channel> channels;
  private Collection<Connector> connectors;

  public MyServerBootStrap(List<Connector> connectors) {
    this.ctx = ApplicationContext.getInstance();
    this.config = ctx.getMainConfig();
    this.connectors = connectors;
    this.channels = new ArrayList<>(connectors.size());
  }

  public void run(Connector connector) throws RuntimeException, IOException, InterruptedException {
    EventLoopGroup bossGroup = new NioEventLoopGroup(config.getInt("http_acceptor_threads", 0));
    EventLoopGroup workerGroup = new NioEventLoopGroup(config.getIntError("http_server_threads"));

    final SocketAddress addr;
    final ChannelFactory<ServerChannel> channelFactory;
    if (connector.geEndpoint().isInherited()) {
      System.out.println(
          "Trying to bootstrap inherited channel: " + connector.geEndpoint().getDescription());
      ServerSocketChannel channel = (ServerSocketChannel) System.inheritedChannel();

      addr = channel.getLocalAddress();
      System.out.println("Channel localAddress(): " + addr);
      channelFactory = new MyChannelFactory(channel);
    } else {
      System.out.println(
          "Trying to bootstrap regular channel: " + connector.geEndpoint().getDescription());
      addr = connector.geEndpoint().getSocketAdress();
      channelFactory = new MyChannelFactory(null);
    }

    ServerBootstrap b = new ServerBootstrap();
    b
        .group(bossGroup, workerGroup)
        .localAddress(addr)
        .channelFactory(channelFactory)
        .childHandler(new ChannelInitializerRouter(Collections.singletonList(connector)))
        .childOption(ChannelOption.SO_KEEPALIVE, true);

    if (config.contains("tcp_max_syn_backlog")) {
      b.option(ChannelOption.SO_BACKLOG, config.getIntError("tcp_max_syn_backlog"));
    }

    Channel serverChannel = b.bind().sync().channel();
    channels.add(serverChannel);
  }

  public void run() throws RuntimeException {
    try {
      for (Connector connector : connectors) {
        run(connector);
      }
      for (Channel channel : channels) {
        channel.closeFuture().sync();
      }
    } catch (Throwable exc) {
      throw new RuntimeException("Failed to start web-server", exc);
    } finally {
      // TODO: fix this
      // workerGroup.shutdownGracefully();
      // bossGroup.shutdownGracefully();
    }
  }
}


class MyChannelFactory implements io.netty.channel.ChannelFactory<ServerChannel> {

  private ServerSocketChannel channel;

  public MyChannelFactory(ServerSocketChannel ch) {
    this.channel = ch;
  }

  @Override
  public ServerChannel newChannel() {
    if (channel == null) {
      return new NioServerSocketChannel();
    } else {
      return new NioServerSocketChannel(channel);
    }
  }

}

日志:

 Trying to bootstrap inherited channel: public (tcp port: 8080)
 Channel localAddress(): /0:0:0:0:0:0:0:0:8080
 java.lang.RuntimeException: Failed to start web-server
         at MyServerBootStrap.run(MyServerBootStrap.java:85)
         at MyServer.run(MyServer.java:61)
         at Main.start(Main.java:96)
         at Main.main(Main.java:165)
 Caused by: java.nio.channels.AlreadyBoundException
         at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:216)
         at sun.nio.ch.InheritedChannel$InheritedServerSocketChannelImpl.bind(InheritedChannel.java:92)
         at io.netty.channel.socket.nio.NioServerSocketChannel.doBind(NioServerSocketChannel.java:128)
         at io.netty.channel.AbstractChannel$AbstractUnsafe.bind(AbstractChannel.java:558)
         at io.netty.channel.DefaultChannelPipeline$HeadContext.bind(DefaultChannelPipeline.java:1338)
         at io.netty.channel.AbstractChannelHandlerContext.invokeBind(AbstractChannelHandlerContext.java:501)
         at io.netty.channel.AbstractChannelHandlerContext.bind(AbstractChannelHandlerContext.java:486)
         at io.netty.channel.DefaultChannelPipeline.bind(DefaultChannelPipeline.java:999)
         at io.netty.channel.AbstractChannel.bind(AbstractChannel.java:254)
         at io.netty.bootstrap.AbstractBootstrap.run(AbstractBootstrap.java:366)
         at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
         at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
         at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
         at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:886)
         at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
         at java.lang.Thread.run(Thread.java:748)

是的,应该可以。

NioServerSocketChannel 允许您通过其构造函数包装现有的 Channel。因此,您需要做的就是编写您自己的 ChannelFactory 并将其与 ServerBootstrap 一起使用,以确保您创建一个包装它的 NioServerSocketChannel

另一种方法是根本不使用 ServerBootstrap,而只是用自己手动创建的 NioServerSocketChannel 调用 register 等。