【问题标题】:what's the sequence of channelhandler in netty?netty中channelhandler的顺序是什么?
【发布时间】:2015-01-15 20:00:10
【问题描述】:

我有一个使用 netty 来实现服务器端和客户端的应用程序。服务器端将当前时间发送给客户端。

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("in timeserverhandler");
        ChannelFuture f = ctx.writeAndFlush(new UnixTime());
        f.addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

编码器:

  public class TimeEncoder extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("in timeencoder");
        UnixTime m = (UnixTime) msg;
        ByteBuf encoded = ctx.alloc().buffer(4);
        encoded.writeInt(m.value());
        ctx.write(encoded, promise); // (1)
    }


}

公共类时间服务器 { 私有静态最终 int PORT = 9000;

public static void main(String[] args) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) {
                        ChannelPipeline p = ch.pipeline();
                        p.addLast(new TimeEncoder(), new TimeServerHandler());
                       //p.addLast(new TimeServerHandler(), new TimeEncoder());

                    }
                });
        ChannelFuture f = b.bind(PORT).sync();
        f.channel().closeFuture().sync();
    } finally {
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

}

}

在 TimeServer 中,如果我将addList 序列更改为注释行,则永远不会调用编码器处理程序,并且客户端无法打印出当前时间。为什么会这样,管道中处理程序的执行顺序是什么?

【问题讨论】:

    标签: java netty


    【解决方案1】:

    佩德罗是对的。

    您通常可以先插入解码器,然后是编码器,最后是您的应用程序处理程序。

    一般的逻辑是:解码器后编码器

    如果您有多个编解码器逻辑(例如,第一个编解码器后面必须跟着第二个编解码器,中间有一个处理程序),那么逻辑将是:

    • pipeline.addLast(decoderProtocol1, encoderProtocol1) 最后是 .addLast(intermediaryHandler1)
    • pipeline.addLast(decoderProtocol2, encoderProtocol2) 最后是 .addLast(intermediaryHandler2)
    • ...
    • pipeline.addLast(decoderProtocoln, encoderProtocoln)
    • pipeline.addLast(finalHandler)

    一些解码器/编码器还带有一个处理程序,作为编解码器,那么显然您只需将pipeline.addLast(decoderProtocoln, encoderProtocoln) 替换为pipeline.addLast(codecProtocoln)

    文档的正确链接是: http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html

    【讨论】:

    • johnstlr 的回答原则上是正确的,但默认情况下不应使用 channel.write 而是 context.write ,就像你所做的那样,主要是因为你写消息的地方可能会改变行为(特别是如果您有多个编解码器)以及出于性能原因。所以应该把解码器/编码器放在第一位的原因,在应用程序处理程序之前。
    【解决方案2】:

    简短回答:您的注释行不起作用的原因是 netty 不理解 java 对象 (UnixTime())。它只理解二进制数据(ByteBuf)。

    说明: 现在处理程序执行的顺序取决于您在管道中添加它们的顺序。对于入站数据,处理程序从头到尾执行。对于出站数据,处理程序从最后一个到第一个执行。现在在处理程序执行期间,netty 检查您的处理程序是否能够处理入站/出站数据。这是通过检查您的处理程序是否扩展 ChannelInboundHandlerAdapter 或/和 ChannelOutboundHandlerAdapter 来完成的。如果它扩展了 ChannelInboundHandlerAdapter,那么将对入站数据执行相同的操作。或者,如果扩展 ChannelOutboundHandlerAdapter,它将针对出站数据执行。

    现在,在工作代码中,您的第一个处理程序是编码器(处理出站事件),第二个处理程序是编写 java 对象(处理入站事件)。在这种情况下,只要通道变为活动状态,就会生成入站事件,并将相同的事件传递给管道中的第一个处理程序,这是您的最后一个处理程序。现在,该处理程序将使用该事件并在作为出站事件的通道上写入数据。现在,这个出站甚至会向上游传播并进入管道中的下一个处理程序,该处理程序处理出站事件,即您的编码器。现在,编码器将 unixtime 转换为二进制数据,并将其传递到通道上。

    现在,在非工作代码中,当您颠倒处理程序的顺序时,一旦通道变为活动状态,入站事件将传递给您的第一个出站事件处理程序,这是管道中的最后一个处理程序(从头开始但最后从结束 )。一旦,这个处理程序生成 unix 时间,它将生成出站事件,该事件将进一步向上游传播。但是在此之后,没有上游事件处理程序来消耗出站事件,因此您的 unix 时间将永远不会转换为二进制数据,因此这不起作用。

    希望它澄清。

    【讨论】:

      【解决方案3】:

      在创建管道时,我总是输入“addLast(decoder, encoder, handler)”。

      在“构建管道”部分查看:http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html

      【讨论】:

        【解决方案4】:

        这是因为您使用的是 ChannelHandlerContext.write 而不是 Channel.write。 http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html 声明 ChannelHandlerContext 方法将事件转发到管道中的下一个处理程序(上游或下游,取决于事件类型),而不是从管道开始。在您的代码中,管道是

        • 网络
        • 时间编码器
        • 时间服务器处理程序

        这很好,但注释代码是

        • 网络
        • 时间服务器处理程序
        • 时间编码器

        当 TimeEncoder 位于 TimeServerHandler 的上游时,它会尝试直接写回网络。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多