Netty的InboundHandler 和OutboundHandler

一、InboundHandler 和OutboundHandler的区别

在Netty中,"inbound"表示来自外部来源(如网络连接)的数据,而"outbound"则表示从应用程序发送到外部目标(如网络连接或其他服务)的数据。

"Inbound"主要涉及应用程序接收和处理外部数据的过程。这包括从网络连接读取数据、解码处理数据、执行业务逻辑等操作。例如,在一个服务器应用程序中,inbound操作可能涉及监听和接受客户端连接,读取客户端发送的请求数据,并将其转发给适当的处理程序进行处理。inbound监听的事件如下:

  • ++channelRegistered/channelUnregistered++
  • ++channelActive/channelInactive++
  • ++channelRead++
  • ++channelReadComplete++
  • ++channelWritabilityChanged++
  • ++userEventTriggered++
  • ++exceptionCaught++

"Outbound"主要涉及应用程序发送数据到外部目标的过程。这包括将数据编码为网络传输的格式、通过网络连接发送数据、处理发送的数据等操作。例如,在一个客户端应用程序中,outbound操作可能涉及将请求数据编码为适当的传输协议,通过网络连接发送给服务器,并等待响应数据。outbound监听的事件如下:

  • ++bind++
  • ++connect++
  • ++disconnect++
  • ++close++
  • ++deregister++
  • ++read++
  • ++write++
  • ++flush++

Netty的Pipeline采用责任链设计模式,责任链的每个节点是ChannelHandlerContext,通过prev和next节点实现双向链表。ChannelHandlerContext对象封装了ChannelHandler对象。Pipeline的首尾节点分别是io.netty.channel.DefaultChannelPipeline.HeadContext和io.netty.channel.DefaultChannelPipeline.TailContext。

Pipeline在分发事件的时候,会根据事件类型选择合适的handler(Inbound/Outbound本身会通用掩码位注册监听的事件类型),可参考 《Netty之ChannelHandlerMask详解》

总结:

  • InboundHandler处理从网络接收的数据,负责解析和处理输入数据。
  • OutboundHandler处理向网络发送的数据,负责封装和处理输出数据。
  • InboundHandler和OutboundHandler在处理数据的方向上有所区别,但它们通常一起使用,通过ChannelPipeline连接在一起,形成一个完整的数据处理链。

二、客户端消息在handler的流向

我们通过简单的例子来说明

2.1注册一个简单的Netty服务器

java 复制代码
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;

public class EchoServer {

    private int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws Exception{
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        for (int i = 0; i < 3; i++) {
                            pipeline.addLast(new InboundHandler(i + 1));
                        }
                        for (int i = 0; i < 3; i++) {
                            pipeline.addLast(new OutboundHandler(i + 1));
                        }
                        System.out.println("handler注册顺序:");
                        pipeline.names().forEach(x -> {
                            System.out.println("handler:" + x);
                        });
                    }
                })
                .option(ChannelOption.SO_BACKLOG,100000)
                .childOption(ChannelOption.SO_KEEPALIVE,true);
        bootstrap.bind(port).sync();
        System.out.println("服务启动,监听端口@"+port);

    }

    public static void main(String[] args)  throws Exception {
        new EchoServer(8001).start();
    }

    static class InboundHandler extends ChannelInboundHandlerAdapter {

        private int index;

        public InboundHandler(int index) {
            this.index = index;
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("InboundChannel["+index+"]接收消息");
            ctx.fireChannelRead(msg);
            if (index == 3) {
                ctx.channel().write("df");
            }
        }

    }

    static class OutboundHandler extends ChannelOutboundHandlerAdapter {

        private int index;

        public OutboundHandler(int index) {
            this.index = index;
        }


        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            System.out.println("OutboundChannel["+index+"]发送消息");
            ctx.write(msg, promise);
        }
    }

}

在这个例子,启动一个简单的socket服务器,并依次注册了三个inboundhandler,三个outboundhandler。启动之后,通过简单的telnet命令,往socket发送数据

2.2使用telnet发送数据

使用windows自带的telnet,输入命令:telnet localhost 8001,然后随便输入一个字符

(部分系统需要通过按"Win +R"快捷键,打开"运行"对话框,输入"optionalfeatures"后按回车键;在打开的"Win dows功能"窗口中,找到并勾选"Telnet客户端")

2.3服务端响应

从这个例子,可以看出

InboundHandler是按照Pipleline的加载顺序,顺序执行;

而OutboundHandler是按照Pipleline的加载顺序,逆序执行。

需要注意的是,由于pipeline采取的是责任链方式,在任何一个节点,如果没有把事件往下传,事件就会在本节点终止。

java 复制代码
   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("InboundChannel["+index+"]接收消息");
            ctx.fireChannelRead(msg);  //注释次代码,则事件不会往下个handler传递数据
        
   }
相关推荐
马尚道2 天前
【韩顺平】尚硅谷Netty视频教程
netty
马尚道2 天前
Netty核心技术及源码剖析
源码·netty
moxiaoran57532 天前
java接收小程序发送的protobuf消息
websocket·netty·protobuf
马尚来3 天前
尚硅谷 Netty核心技术及源码剖析 Netty模型 详细版
源码·netty
马尚来3 天前
Netty核心技术及源码剖析
后端·netty
失散139 天前
分布式专题——35 Netty的使用和常用组件辨析
java·分布式·架构·netty
hanxiaozhang201811 天前
Netty面试重点-1
网络·网络协议·面试·netty
mumu1307梦21 天前
SpringAI 实战:解决 Netty 超时问题,优化 OpenAiApi 配置
java·spring boot·netty·超时·timeout·openapi·springai
9527出列1 个月前
Netty源码分析--Reactor线程模型解析(二)
netty
若水不如远方1 个月前
Netty的四种零拷贝机制:深入原理与实战指南
java·netty