netty学习系列三:pipeline原理与事件处理

零、目录

一、pipeline整体关系简述

二、Unsafe的作用

三、事件的分类及处理

四、pipeline中节点的添加和删除

一、pipeline整体关系简述

1、netty中的pipeline模型

当EventLoop的selector监听到某Channel产生了就绪的IO事件,并调用socket API对就绪的IO事件进行操作后,需要将操作产生的“IO数据”或“操作结果”告知用户进行相应的业务处理。

netty将因外部IO事件导致的Channel状态变更(Channel被注册到EventLoop中,Channel状态变为可用,Channel读取到IO数据...)或Channel内部逻辑操作(添加ChannelHandler...)抽象为不同的回调事件,并定义了pipeline对Channel的回调事件进行流式的响应处理。

用户可在pipeline中添加多个事件处理器(ChannelHandler),并通过实现ChannelHandler中定义的方法,对回调事件进行定制化的业务处理。ChannelHandler也可以调用自身方法对Channel本身进行操作。

netty会保证“回调事件在ChannelHandler之间的流转”及“Channel内部IO操作”由EventLoop线程串行执行,用户也可以在ChannelHandler中使用自行构建的业务线程进行业务处理。

2、pipeline相关类的关系图

pipeline相关类的关系图

1)DefaultChannelPipeline:

事件处理流,是一个双向链表结构,链表中节点元素为ChannelHandlerContext。

新的AbstractNioChannel创建时,会创建该Channel对应的DefaultChannelPipeline,用于处理该Channel对应的回调事件。

DefaultChannelPipeline创建时,会自动创建并向链表中添加两个ChannelhandlerContext节点——head和tail。

pipeline的fireXXX()方法:回调事件的发起方法。会产生相应回调事件并直接调用ChannelHandlerContext.invokeXXX(head)方法将回调事件传递给pipeline的head节点。

2)ChannelHandlerContext:

事件处理器上下文,pipeline中的实际处理节点。

每个处理节点ChannelHandlerContext中包含一个具体的事件处理器ChannelHandler,同时ChannelHandlerContext中也绑定了对应的pipeline和Channel的信息,方便ChannelHandler进行调用。

AbstractChannelHandlerContext具体实现了ChannelHandlerContext接口的功能,并进行了相应扩展。

ChannelHandlerContext的fireXXX方法:回调事件的发起方法。会产生相应回调事件并将其交给pipeline中的下一个处理节点。此方法提供给用户实现的ChannelHandler使用,用于将回调事件向pipeline中的下一个节点传递。

AbstractChannelHandlerContext的static invokeXXX(AbstractChannelHandlerContext next)方法:封装next.invokeXXX()的逻辑并交给EventLoop的IO线程执行。

ChannelHandlerContext的invokeXXX()方法:回调事件执行方法。执行节点中事件处理器ChannelHandler的XXX方法,实际处理回调事件。

3)ChannelHandler:


ChannelHandler类结构

ChannelHandler(事件处理器接口),由ChannelInboundHandler接口和ChannelOutboundHandler接口继承。

ChannelInboundHandler中定义了各个回调事件的回调方法,由用户进行具体实现。

ChannelOutboundHandler中定义了方法进行Channel内部IO操作(Channel发起bind/connect/close操作,Channel监听OP_READ,Channel写IO数据...),供用户在回调方法中使用。

ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter为接口的默认实现类(其实没干什么事),用户通过继承这两个类来实现自己的业务处理逻辑。

二、Unsafe的作用

1、概述

Unsafe是Channel的内部类,一个Channel对应一个Unsafe。

Unsafe用于处理Channel对应网络IO的底层操作。ChannelHandler处理回调事件时产生的相关网络IO操作最终也会委托给Unsafe执行。

2、类继承结构

Unsafe类继承结构

Unsafe接口中定义了socket相关操作,包括SocketAddress获取、selector注册、网卡端口绑定、socket建连与断连、socket写数据。这些操作都和jdk底层socket相关。

NioUnsafe在Unsafe基础上增加了几个操作,包括访问jdk的SelectableChannel、socket读数据等。

NioByteUnsafe实现了与socket连接的字节数据读取相关的操作。

NioMessageUnsafe实现了与新连接建立相关的操作。

3、读/写操作实现浅述

Unsafe的读写操作

三、事件的分类及处理

1、Inbound事件

用于描述因外部事件导致的Channel状态变更。

Inbound事件列表

ChannelInboundHandler中定义了各个Inbound事件的回调方法。

2、Outbound事件

用于定义Channel能够提供的IO操作。

outbound事件列表

ChannelOutboundHandler中定义了Channel提供的进行IO操作的方法。

3、发起并处理inbound事件

ChannelInboundInvoker接口定义了inbound事件的发起方法fireXXX(),用于发起一个inbound事件。

ChannelPipeline接口继承了ChannelInboundInvoker,其实现类DefaultChannelPipeline因此具有发起inbound事件的功能。-->  实际通过调用AbstractChannelHandlerContext的静态invokeXXX()方法,产生并从pipeline的head节点开始接收事件。

ChannelHandlerContext接口继承了ChannelInboundInvoker,其实现类AbstractChannelhandlerContext因此具有发起inbound事件的功能。 -->  实际通过调用AbstractChannelHandlerContext的静态invokeXXX()方法,从当前节点的下一个节点开始接收事件。

AbstractChannelHandlerContext类中定义了静态invokeXXX(next)方法,用于在指定节点上开始接收inbound事件,并保证接收过程在EventLoop线程中执行。

AbstractChannelHandlerContext类中定义了invokeXXX()方法,用于实际接收inbound事件,并执行其ChannelHandler实现的事件回调方法。

对于inbound事件,因为需要进行业务逻辑处理,因此pipeline的head节点会执行fireXXX()方法将事件透传给后面的用户自己实现inbound处理节点,由用户自己实现的ChannelHandler接收事件并回调执行业务逻辑。

inbound事件的发起与处理过程

fireChannelActive、fireChannelRead、fireChannelReadComplete等inbound事件的处理过程可以自己跟代码看一下。(head节点对channelRead事件向后透传。对channelActive和channelReadComplete事件,向后传递并产生了Channel.read()的outbound事件)


4、发起并处理outbound事件

ChannelOutboundInvoker接口定义了outbound事件的产生方法(register,bind,connect,read,write,flush...),用于描述Channel能够提供的IO操作。

Channel接口继承了ChannelOutboundInvoker,其实现类AbstractChannel因此具有执行outbound事件的功能。-->  实际通过调用ChannelPipeline的对应方法实现。

ChannelPipeline接口继承了ChannelOutboundInvoker,其实现类DefaultChannelPipeline因此具有执行outbound事件的功能。-->  实际通过调用pipeline的tail节点的对应方法实现。

ChannelHandlerContext接口继承了ChannelOutboundInvoker,其实现类AbstractChannelhandlerContext因此具有执行outbound事件的功能。-->  实际从当前节点的前一个outbound处理节点开始接收事件,调用其invokeXXX()方法,并保证接收过程在EventLoop线程中执行。

AbstractChannelHandlerContext类中定义了invokeXXX()方法,用于实际接收outbound事件,并执行其ChannelHandler实现的事件执行方法。

对于outbound事件,因为和IO操作相关,最后会由pipeline中的head节点接收处理。head节点实现了ChannelHandler的事件执行方法,将实际的执行操作委托给Unsafe进行。

outbound事件的发起与处理过程

bind、connect、read、writeAndFlush等outbound事件的处理过程可以自己跟代码看一下。(最终由head节点委托给Unsafe类执行相关IO操作)

5、head、tail节点的作用


pipeline

head节点:

head节点既是inBound处理节点,又是outBound处理节点。

head节点作为pipeline的头结点开始接收并传递inbound事件。并作为pipeline的最后一环最终接收处理outbound事件(委托Unsafe进行outbound事件的相关IO操作)。

tail节点:

tail节点是inBound处理节点。

tail节点作为pipeline的第一环传递outbound事件,其实就是将outbound事件透传到前一个outbound处理节点。并作为pipeline的最后一环最终接收inbound事件,大部分左右是终止inbound事件的传播。

tail节点的exceptionCaught方法:若最终在用户自定义的处理节点没有捕获处理异常,则在tail节点捕获异常打印警告日志。

tail节点的channelRead方法:若Channel读入的ByteBuf在流经pipeline过程中没有被消费掉,最终流入了tail节点,则将该ByteBuf丢弃回收并打印警告日志。

四、pipeline中节点的添加和删除

1、向pipeline中添加ChannelHandler的过程

从DefaultChannelPipeline.addLast(ChannelHandler... handlers)这行代码开始跟吧

细节不想写了,列一下大致流程:

1)checkMultiplicity(handler);  -->  检查要添加的handler是否被重复添加

2)newCtx= newContext(group,filterName(name,handler),handler);  -->  创建节点

3)addLast0(newCtx);  --> 添加节点到pipeline的末尾(tail之前)

4)callHandlerAdded0(newCtx);  -->  回调新建节点的handlerAdded(ctx)方法

2、将ChannelHandler从pipeline中移除的过程

从DefaultChannelPipeline.remove(ChannelHandler handler)这行代码开始跟吧

细节不想写了,列一下大致流程:

1)getContextOrDie(handler)  -->  遍历pipeline链表找到待删除的接待对象

2)remove0(ctx)  --> 将节点从pipeline链表中移除

3)callHandlerRemoved0(ctx)    --> 回调移除节点的handlerRemoved(ctx)方法。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容