Netty源码深度解析-Pipeline(2) 以客户端为例分析Pipeline工作原理

本文深入分析Netty的Pipeline在客户端的工作原理,以建立连接、收发数据为例,探讨ChannelHandler、ChannelInboundInvoker和ChannelOutboundInvoker,以及connect、write等操作的传播过程,揭示Pipeline如何处理事件和命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读

原创文章,转载请注明出处。

本文源码地址:netty-source-code-analysis

本文所使用的netty版本4.1.6.Final:带注释的netty源码

在"Pipeline的构造"这一节中我们已经讲过了,Pipeline中利用了责任链模式,而发挥责任链功能的数据结构就是由多个HandlerContext构成的的双向链表,而每一个HandlerContext又对应一个ChannelHandler(组合模式或者继承)。由于每一个HandlerContext对应一个ChannelHandler,所以本文行文中有时候会把HandlerContextChannelHandler等同对待,例如文章中

从当前HandlerContexttail)开始向head方向遍历寻找下一个ChannelOutboundHandler

实际上准确来说应该是

从当前HandlerContexttail)开始向head方向遍历寻找下一个HandlerContext,并且该HandlerContext包含或者实现了ChannelOutboundHandler

为了行文不那么拗口,我们就把HandlerContextChannelHandler等同对待了。

本文我们以客户端建立连接、收发数据为例来学习一下Pipeline的工作原理。

1 ChannelHandler方法概览

ChannelPipeline的构造这篇文章中我们提到过ChannelHandlerPipeline中双向链表由HandlerContext组成,而每一个HandlerContext又包含一个ChannleHandler。我们看一下其中的主要方法,这是所有ChannelHandler的公共接口,公共接口中主要有两个方法handlerAddedhandlerRemoved,这是在ChannelHandler添加和删除完成之后的回调方法。上一篇文章中我们已经分析过PipelineChannelHandler的添加和删除了,这里不再赘述。

public interface ChannelHandler {
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
     @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
}

我们重点来看ChannelHandler的两个子接口,ChannelInboundHandlerChannelOutboundHandler
首先来看ChannelInboundHandlerChannelInboundHandler类上的注释如下

which adds callbacks for state changes. This allows the user
to hook in to state changes easily.

翻译过来就是“Channel状态改变时的回调方法”。其中的方法名多为Channel + 动词过去分词的形式。

public interface ChannelInboundHandler extends ChannelHandler {
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
    void channelActive(ChannelHandlerContext ctx) throws Exception;
    void channelInactive(ChannelHandlerContext ctx) throws Exception;
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

再来看ChannelOutboundHandlerChannelOutboundHandler上的注释如下

which will get notified for IO-outbound-operations.

翻译过来就是“当发生IO出站操作时的回调方法”,其中的方法名多为动词原型的形式。

public interface ChannelOutboundHandler extends ChannelHandler {
   
   
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
    void connect(
            ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    void read(ChannelHandlerContext ctx) throws Exception;
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    void flush(ChannelHandlerContext ctx) throws Exception;
}

2 ChannelInboundInvoker和ChannelOutboundInvoker

和本文相关的组件中,ChannelPipelineChannelHandlerContext都实现了ChannelInboundInvokerChannelOutboundInvoker接口。
ChannelInboundInvoker和ChannelOutboundInvoker UML类图
ChannelInboundInvoker内的方法的特点是大部分都是以fire开头,并且是fire + 动词的过去分词的形式。我们知道过去分词是表被动的意思,所以这里呢,一般是Channel里发生了某些“被动事件”之后调用的。

public interface ChannelInboundInvoker {
   
   
    ChannelInboundInvoker fireChannelRegistered();
    ChannelInboundInvoker fireChannelUnregistered();
    ChannelInboundInvoker fireChannelActive();
    ChannelInboundInvoker fireChannelInactive();
    ChannelInboundInvoker fireExceptionCaught(Throwable cause);
    ChannelInboundInvoker fireUserEventTriggered(Object event);
    ChannelInboundInvoker fireChannelRead(Object msg);
    ChannelInboundInvoker fireChannelReadComplete();
    ChannelInboundInvoker fireChannelWritabilityChanged();
}

ChannelOutboundInvoker内的方法的特点是除了后边几个以new开头的方法之外,都是以单个或者两个动词原型的形式作为方法名称,比如readwritewriteAndFlush。这里直接用动词原型就是表示主动的意思,所以这里一般是向Channel发送主动命令使用的。

public interface ChannelOutboundInvoker {
   
   
    ChannelFuture bind(SocketAddress localAddress);
    ChannelFuture connect(SocketAddress remoteAddress);
    ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);
    ChannelFuture disconnect();
    ChannelFuture close();
    ChannelFuture deregister();
    ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
    ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);
    ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
    ChannelFuture disconnect(ChannelPromise promise);
    ChannelFuture close(ChannelPromise promise);
    ChannelFuture deregister(ChannelPromise promise);
    ChannelOutboundInvoker read();
    ChannelFuture write(Object msg);
    ChannelFuture write(Object msg, ChannelPromise promise);
    ChannelOutboundInvoker flush();
    ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
    ChannelFuture writeAndFlush(Object msg);
    ChannelPromise newPromise();
    ChannelProgressivePromise newProgressivePromise();
    ChannelFuture newSucceededFuture();
    ChannelFuture newFailedFuture(Throwable cause);
    ChannelPromise voidPromise();

我们来仔细分析一下上文中提到的ChannelInboundHandlerChannelInboundInvokerChannelOutboundHandlerChannelOutboundInvoker
我们看到ChannelInboundHandlerChannelInboundInvoker中的方法非常相似,ChannelInboundHandler中的方法名多以Channel加动词过去分词的形式组成,而ChannelInboundInvoker中的方法就是ChannelInboundHandler中的方法加上fire构成。

ChannelOutboundHandlerChannelOutboundInvoker中的方法名几乎一模一样,都是以动词原形作为方法名。

我们来仔细琢磨一下这些方法的命名,Channel加动词过去分词表示该Channel发生了某种事件,而fireChannel加动词过去分词表示在该Channel上触发这种事件。例如ChannelRead表示该Channel读到了数据事件,fireChannelRead表示在该Channel上触发ChannelRead事件。

直接用动词原形作为方法名,表示命令该Channel进行某种操作,例如read表示命令该Channel进行数据的读取。为什么ChannelOutboundInvoker中的方法名没有以fire开头呢,因为ChannelOutbound传播的不是事件,而是一种主动的操作。

还有同学记得我在“Netty整体架构”这篇文章中提到的“事件”和“命令”吗,在某些地方翻译成“入站事件”和“出站事件”,我个人认为翻译成“事件”和“命令”更容易理解,因为我们感知到“事件”的发生是被动的,而发出“命令”是主动的,都翻译成“事件”不容易理解。

3 以客户端为例分析Pipeline工作原理

本文的示例代码在package com.zhongdaima.netty.analysis.pipeline中。
服务端示例代码如下,这个服务端的功能就是将所有客户端发过来的数据原封不动地返回。

/**
 * 欢迎关注公众号“种代码“,获取博主微信深入交流
 *
 * @author wangjianxin
 */
public class ServerBoot {
   
   
    public static void main(String[] args) throws InterruptedException {
   
   
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(1);
        try {
   
   
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
   
   
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
   
   
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
   
   
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
   
                                    //连接发过来的数据原样返回
                                    if (msg instanceof ByteBuf) {
   
   
                                        ctx.write(msg);
                                        ctx.flush();
                                    }
                                }

                            });
                        }
                    });
            ChannelFuture f = b.bind(8000).sync(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值