netty从入门到放弃—channelHandler与pipeline

pipeline

在netty的客户端和服务端的链接中,处理链接接入的是boss线程组,接入后的链接逻辑处理,要经过worker线程组进行处理,每一条链接在netty里面对应着一个channel,channel中处理不同的逻辑也对应了不同的处理器,叫handler,这些handler都存在在一个pipeline中;每个channel对应一个pipeline,pipeline是一个双向链表结构,对应着所有输入输出的消息逻辑处理;类似Tomcat的filterChain。
在这里插入图片描述

ChannelHandler

pipeline中存放着各种用于处理对应客户端逻辑的处理器,这些处理器就是对应的ChannelHandler,它们的实现结构如下

在这里插入图片描述
ChannelHandler实现分为inbound和outbound,子接口分别是ChannelInboundHandler和ChannelOutboundHandler。
其中inbound是进行客户端请求逻辑处理,当客户端有请求到达服务端时,会由inbound相关的handler进行接收,然后调用其中的channelRead()方法进行对应的处理逻辑。

outbound是指服务端向客户端写数据时进行封装的逻辑处理,比如我们服务端中封装具体的消息实体,经过各种outbound进行封装、加密、编码,转换为一个ByteBuf后,才写到客户端的;其中outbound中写数据的方法是write()。

由于这些接口里面需要实现比较多接口方法,netty很贴心地为我们实现了几个默认的适配器类,其中ChannelHandlerAdapter类是ChannelHandler默认实现类,ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter是对inbound和outbound的默认实现类

ChannelHandler在pipeline的事件传递

通过代码来实际观测ChannelHandler的实际事件传递,我们定义了ChannelInboundHandlerAdapterA、ChannelInboundHandlerAdapterB、ChannelInboundHandlerAdapterC和对应的ChannelOutboundHandlerAdapterA、ChannelOutboundHandlerAdapterB、ChannelOutboundHandlerAdapterC观测下结果

NettyServer

serverBootstrap.group(bossGroup, workerGroup)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childOption(ChannelOption.TCP_NODELAY, true)
        .childOption(ChannelOption.SO_KEEPALIVE, true)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) {
                // inbound
                ch.pipeline().addLast(new ChannelInboundHandlerA());
                ch.pipeline().addLast(new ChannelInboundHandlerB());
                ch.pipeline().addLast(new ChannelInboundHandlerC());
                // outbound
                ch.pipeline().addLast(new ChannelOutboundHandlerA());
                ch.pipeline().addLast(new ChannelOutboundHandlerB());
                ch.pipeline().addLast(new ChannelOutboundHandlerC());
            }
        });

每个ChannelInboundHandler和ChannelOutboundHandler都继承了对应的Adapter类,重写了对应的ChannelRead()和write方法。

inbound

@Slf4j
public class ChannelInboundHandlerAdapterA extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("消息传递到 ChannelInboundHandlerA ,消息内容: {}", byteBuf.toString(StandardCharsets.UTF_8));
        super.channelRead(ctx, msg);
    }
}
@Slf4j
public class ChannelInboundHandlerAdapterB extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("消息传递到 ChannelInboundHandlerB ,消息内容: {}", byteBuf.toString(StandardCharsets.UTF_8));
        super.channelRead(ctx, msg);
    }
}
@Slf4j
public class ChannelInboundHandlerAdapterC extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("消息传递到 ChannelInboundHandlerC ,消息内容: {}", byteBuf.toString(StandardCharsets.UTF_8));

        byteBuf.clear();
        byteBuf.writeBytes("hello client".getBytes());
        ctx.channel().writeAndFlush(byteBuf);
    }
}

在每个ChannelOutboundHandlerAdapter的channelRead方法中,不仅打印了当前类的信息,并且调用了父类的对应channelRead方法,把事件传递到下一个handler中;

outbound

@Slf4j
public class ChannelOutboundHandlerAdapterA extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("返回消息传到[ChannelOutboundHandlerA], 内容为: {}", byteBuf.toString(Charset.forName("utf-8")));
        super.write(ctx, msg, promise);
    }
}
@Slf4j
public class ChannelOutboundHandlerAdapterB extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("返回消息传到[ChannelOutboundHandlerB], 内容为: {}", byteBuf.toString(StandardCharsets.UTF_8));
        super.write(ctx, msg, promise);
    }
}
@Slf4j
public class ChannelOutboundHandlerAdapterC extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        log.info("返回消息传到[ChannelOutboundHandlerC], 内容为: {}", byteBuf.toString(StandardCharsets.UTF_8));
        super.write(ctx, msg, promise);
    }
}

在每个ChannelOutboundHandler的write方法中,打印了当前类信息,并且调用了父类的write方法,进行事件传递,把事件传递到下一个handler中。

代码执行如下

INFO  mxdshop.xyz.netty.server.handler.ChannelInboundHandlerAdapterA 17 - 消息传递到 ChannelInboundHandlerA ,消息内容: hello server
INFO  mxdshop.xyz.netty.server.handler.ChannelInboundHandlerAdapterB 16 - 消息传递到 ChannelInboundHandlerB ,消息内容: hello server
INFO  mxdshop.xyz.netty.server.handler.ChannelInboundHandlerAdapterC 17 - 消息传递到 ChannelInboundHandlerC ,消息内容: hello server
INFO  mxdshop.xyz.netty.server.handler.ChannelOutboundHandlerAdapterC 18 - 返回消息传到[ChannelOutboundHandlerC], 内容为: hello client
INFO  mxdshop.xyz.netty.server.handler.ChannelOutboundHandlerAdapterB 17 - 返回消息传到[ChannelOutboundHandlerB], 内容为: hello client
INFO  mxdshop.xyz.netty.server.handler.ChannelOutboundHandlerAdapterA 17 - 返回消息传到[ChannelOutboundHandlerA], 内容为: hello client

咦,明明outbound的定义顺序跟inbound是一样的,为什么outbound的handler返回顺序却跟inbound的反过来了,也就是跟它们定义的顺序反过来了。我们单独把pipeline的结构拆出来展示

在这里插入图片描述
pipeline是一个双向列表,Inbound和outbound是分别注册进去了,所以实际上的执行顺序为

在这里插入图片描述
Inbound和outbound是定义在同一个pipeline中,但是对于他们的调用方式是分开的,Inbound只会把context传播到下一个Inbound中,而outbound的context是传播到下一个outbound中,两者互不干扰。

代码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值