一. ChannelPipeline 接口
1.1 介绍
ChannelPipeline
相当于 ChannelHandler
的集合,用于处理或拦截Channel
的入站事件和出站操作。
ChannelPipeline
实现了拦截过滤器模式的高级形式,让用户完全控制事件的处理方式以及管道中的ChannelHandler
如何相互交互。- 每个
Channel
都有自己独有的管道ChannelPipeline
,在创建新通道时自动创建。
这里就有两个问题:
-
ChannelPipeline
是如何管理ChannelHandler
集合? -
ChannelHandler
集合是如何处理I/O
事件?
1.1.1 管理ChannelHandler
集合
ChannelPipeline
管理ChannelHandler
集合,是利用 ChannelHandler
创建一个上下文对象 ChannelHandlerContext
, 而ChannelPipeline
存储的就是这个上下文对象。
这个 ChannelHandlerContext
对象下一节将重点讲解。
1.1.2 流程图
ChannelPipeline
管理的 ChannelHandler
集合处理I/O
事件的流程图如下:
|
+---------------------------------------------------+---------------+
| ChannelPipeline | |
| \|/ |
| +---------------------+ +-----------+----------+ |
| | Inbound Handler N | | Outbound Handler 1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler N-1 | | Outbound Handler 2 | |
| +----------+----------+ +-----------+----------+ |
| /|\ . |
| . . |
| ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
| [ method call] [method call] |
| . . |
| . \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 2 | | Outbound Handler M-1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 1 | | Outbound Handler M | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
+---------------+-----------------------------------+---------------+
| \|/
+---------------+-----------------------------------+---------------+
| | | |
| [ Socket.read() ] [ Socket.write() ] |
| |
| Netty Internal I/O Threads (Transport Implementation) |
+-------------------------------------------------------------------+
可以看出 ChannelPipeline
将管理的 ChannelHandler
分为两种:
-
ChannelInboundHandler
处理入站事件- 入站事件是被动接收事件,例如接收远端数据,通道注册成功,通道变的活跃等等。
- 入站事件流向是从
ChannelPipeline
管理的ChannelInboundHandler
列表头到尾。因为入站事件一般都是从远端发送过来,所以流向是从头到尾。 - 采用拦截器的模式,由
ChannelInboundHandler
决定是否处理列表中的下一个ChannelInboundHandler
。
-
ChannelOutboundHandler
处理出站事件- 出站事件是主动触发事件,例如绑定,注册,连接,断开,写入等等。
- 出站事件流向是从
ChannelPipeline
管理的ChannelOutboundHandler
列表尾到头。因为出站事件最后要发送到远端,所以从尾到头。 - 采用拦截器的模式,由
ChannelInboundHandler
决定是否处理列表中的下一个ChannelInboundHandler
(因为是从尾到头,这里的下一个,在列表中其实是上一个)。
1.2 源码
public interface ChannelPipeline
extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable> {
/**
* 在这个管道ChannelPipeline的开头插入给定的ChannelHandler。
*
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified handler is {@code null}
*/
ChannelPipeline addFirst(String name, ChannelHandler handler);
/**
* 在这个管道ChannelPipeline的开头插入给定的ChannelHandler。
*
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified handler is {@code null}
*/
ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
/**
* 在管道的最后追加给定的 ChannelHandler。
*
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified handler is {@code null}
*/
ChannelPipeline addLast(String name, ChannelHandler handler);
/**
* 在管道的最后追加给定的 ChannelHandler。
*
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified handler is {@code null}
*/
ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);
/**
* 在此管道的现有ChannelHandler(名称是baseName)之前插入ChannelHandler。
*
* @throws NoSuchElementException
* if there's no such entry with the specified {@code baseName}
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified baseName or handler is {@code null}
*/
ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
/**
* 在此管道的现有ChannelHandler(名称是baseName)之前插入ChannelHandler。
*
* @throws NoSuchElementException
* if there's no such entry with the specified {@code baseName}
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified baseName or handler is {@code null}
*/
ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
/**
* 在此管道的现有ChannelHandler(名称是baseName)之后插入ChannelHandler。
*
* @throws NoSuchElementException
* if there's no such entry with the specified {@code baseName}
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified baseName or handler is {@code null}
*/
ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);
/**
* 在此管道的现有ChannelHandler(名称是baseName)之后插入ChannelHandler。
*
* @throws NoSuchElementException
* if there's no such entry with the specified {@code baseName}
* @throws IllegalArgumentException
* if there's an entry with the same name already in the pipeline
* @throws NullPointerException
* if the specified baseName or handler is {@code null}
*/
ChannelPipeline addAfter(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
/**
* 在这个管道ChannelPipeline的开头插入多个 ChannelHandler。
*/
ChannelPipeline addFirst(ChannelHandler... handlers);
/**
* 在这个管道ChannelPipeline的开头插入多个 ChannelHandler。
*/
ChannelPipeline addFirst(EventExecutorGroup group, ChannelHandler... handlers);
/**
* 在管道的最后追加多个 ChannelHandler。
*/
ChannelPipeline addLast(ChannelHandler... handlers);
/**
* 在管道的最后追加多个 ChannelHandler。
*/
ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler... handlers);
/**
* 从管道中删除指定的 ChannelHandler
*
* @throws NoSuchElementException
* if there's no such handler in this pipeline
* @throws NullPointerException
* if the specified handler is {@code null}
*/
ChannelPipeline remove(ChannelHandler handler);
/**
* 从管道中删除指定名称的 ChannelHandler
* 返回被删除的 ChannelHandler 对象
* @throws NoSuchElementException
* if there's no such handler with the specified name in this pipeline
* @throws NullPointerException
* if the specified name is {@code null}
*/
ChannelHandler remove(String name);
/**
* 从管道中删除指定类型的 ChannelHandler
* 返回被删除的 ChannelHandler 对象
*
* @throws NoSuchElementException
* if there's no such handler of the specified type in this pipeline
* @throws NullPointerException
* if the specified handler type is {@code null}
*/
T remove(Class handlerType);
/**
* 删除管道中第一个的 ChannelHandler,并返回删除的 ChannelHandler 对象
* @throws NoSuchElementException
* if this pipeline is empty
*/
ChannelHandler removeFirst();
/**
* 删除管道中最后一个的 ChannelHandler,并返回删除的 ChannelHandler 对象
* @throws NoSuchElementException
* if this pipeline is empty
*/
ChannelHandler removeLast();
/**
* 用新的 newHandler 替换该管道中指定老的 oldHandler。
*
* @throws NoSuchElementException
* if the specified old handler does not exist in this pipeline
* @throws IllegalArgumentException
* if a handler with the specified new name already exists in this
* pipeline, except for the handler to be replaced
* @throws NullPointerException
* if the specified old handler or new handler is
* {@code null}
*/
ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);
/**
* 用新的 newHandler 替换该管道中指定名称老的 ChannelHandler。
* 返回被替换老的 ChannelHandler
*
* @throws NoSuchElementException
* if the handler with the specified old name does not exist in this pipeline
* @throws IllegalArgumentException
* if a handler with the specified new name already exists in this
* pipeline, except for the handler to be replaced
* @throws NullPointerException
* if the specified old handler or new handler is
* {@code null}
*/
ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);
/**
* 用新的 newHandler 替换该管道中指定类型老的 ChannelHandler。
* 返回被替换老的 ChannelHandler
*
* @throws NoSuchElementException
* if the handler of the specified old handler type does not exist
* in this pipeline
* @throws IllegalArgumentException
* if a handler with the specified new name already exists in this
* pipeline, except for the handler to be replaced
* @throws NullPointerException
* if the specified old handler or new handler is
* {@code null}
*/
T replace(Class oldHandlerType, String newName,
ChannelHandler newHandler);
/**
* 返回此管道中的第一个 ChannelHandler。
* 如果管道为空,那么就返回 null
*/
ChannelHandler first();
/**
* 返回此管道中第一个 ChannelHandler 的上下文对象 ChannelHandlerContext。
* 如果管道为空,那么就返回 null
*/
ChannelHandlerContext firstContext();
/**
* 返回此管道中的最后一个 ChannelHandler。
* 如果管道为空,那么就返回 null
*/
ChannelHandler last();
/**
* 返回此管道中最后一个 ChannelHandler 的上下文对象 ChannelHandlerContext。
* 如果管道为空,那么就返回 null
*/
ChannelHandlerContext lastContext();
/**
* 返回此管道中指定名称的 ChannelHandler。
*/
ChannelHandler get(String name);
/**
* 返回此管道中指定类型的 ChannelHandler。
*/
T get(Class handlerType);
/**
* 返回此管道中指定ChannelHandler的上下文对象ChannelHandlerContext。
*/
ChannelHandlerContext context(ChannelHandler handler);
/**
* 返回此管道中指定名称 ChannelHandler 的上下文对象ChannelHandlerContext。
*/
ChannelHandlerContext context(String name);
/**
* 返回此管道中指定类型 ChannelHandler 的上下文对象ChannelHandlerContext。
*/
ChannelHandlerContext context(Class extends ChannelHandler> handlerType);
/**
* 返回此管道依附的通道 Channel。
*/
Channel channel();
/**
* Returns the {@link List} of the handler names.
* 返回此管道拥有的 ChannelHandler 名称的列表。
*/
List names();
/**
* 返回此管道拥有的 ChannelHandler 集合
*/
Map toMap();
// ------- 复写来自 ChannelInboundInvoker 中的方法 ---------//
@Override
ChannelPipeline fireChannelRegistered();
@Override
ChannelPipeline fireChannelUnregistered();
@Override
ChannelPipeline fireChannelActive();
@Override
ChannelPipeline fireChannelInactive();
@Override
ChannelPipeline fireExceptionCaught(Throwable cause);
@Override
ChannelPipeline fireUserEventTriggered(Object event);
@Override
ChannelPipeline fireChannelRead(Object msg);
@Override
ChannelPipeline fireChannelReadComplete();
@Override
ChannelPipeline fireChannelWritabilityChanged();
@Override
ChannelPipeline flush();
}
不要看 ChannelPipeline
方法很多,其实主要分为两类:
- 一类是管理
ChannelHandler
相关方法,比如向管道ChannelPipeline
添加,删除,替换,查找ChannelHandler
方法。 - 一类是继承自
ChannelInboundInvoker
和ChannelOutboundInvoker
方法,用于发送入站和出站事件。
1.2.1 管理 ChannelHandler
1.2.1.1 添加
添加到管道
ChannelPipeline
的ChannelHandler
都要有一个名字,可以根据这个名字在管道中查找到这个ChannelHandler
。
ChannelPipeline addFirst(String name, ChannelHandler handler);
ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addLast(String name, ChannelHandler handler);
ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);
ChannelPipeline addAfter(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
ChannelPipeline addFirst(ChannelHandler... handlers);
ChannelPipeline addFirst(EventExecutorGroup group, ChannelHandler... handlers);
ChannelPipeline addLast(ChannelHandler... handlers);
ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler... handlers);
- 可以向管道
ChannelPipeline
头或者尾插入一个或者多个ChannelHandler
。- 可以在管道
ChannelPipeline
中已存在的某个ChannelHandler
(通过baseName
查找到) ,之前或者之后插入一个ChannelHandler
。
你会发现每个添加方法都有多一个 EventExecutorGroup
参数的对应方法,它作用是什么呢?
- 我们知道通道
Channel
是注册到一个事件轮询器EventLoop
中,通道所有的IO
事件都是通过在这个事件轮询器中获取。- 那么默认情况下,通道
Channel
对应的管道ChannelPipeline
所管理的ChannelHandler
也都是在这个事件轮询器中处理的。- 这就要求
ChannelHandler
不能有太耗时操作,尤其是不能有阻塞操作,这样会导致整个事件轮询器被阻塞,会影响注册到这个事件轮询器所有通道的IO
事件处理,以及这个事件轮询器包含的所有待执行任务和计划任务。- 但是我们真的有这个方面的需求怎么办呢? 因此
ChannelPipeline
提供了多一个EventExecutorGroup
参数的方法,它会将添加的这个ChannelHandler
的所有方法都放在指定的这个事件执行器组EventExecutorGroup
中执行,这样就不会阻塞通道Channel
对应的事件轮询器。
1.2.1.2 删除
ChannelPipeline remove(ChannelHandler handler);
ChannelHandler remove(String name);
T remove(Class handlerType);
ChannelHandler removeFirst();
ChannelHandler removeLast();
- 直接从管道中删除指定的
ChannelHandler
对象- 从管道删除指定名称
name
或者指定类型handlerType
的ChannelHandler
对象,并返回它。- 删除管道头或者尾的
ChannelHandler
对象,并返回它。
1.2.1.3 替换
ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);
ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);
T replace(Class oldHandlerType, String newName,ChannelHandler newHandler);
- 用新的
newHandler
替换该管道中指定老的oldHandler
。- 用新的
newHandler
替换该管道中指定名称oldName
或者指定类型oldHandlerType
对应的老ChannelHandler
, 并返回它。
1.2.1.4 查找
ChannelHandler get(String name);
T get(Class handlerType);
ChannelHandlerContext context(ChannelHandler handler);
ChannelHandlerContext context(String name);
ChannelHandlerContext context(Class extends ChannelHandler> handlerType);
- 根据名字或者类型从管道中查找对应的
ChannelHandler
对象。- 根据
ChannelHandler
或者名字或者类型从管道中查找对应的ChannelHandlerContext
对象。- 其实管道
ChannelPipeline
存储的是ChannelHandlerContext
对象,它是由ChannelHandler
对象创建的,可以通过ChannelHandlerContext
直接获取ChannelHandler
。
1.2.1.5 其他方法
ChannelHandler first();
ChannelHandlerContext firstContext();
ChannelHandler last();
ChannelHandlerContext lastContext();
List names();
Map toMap();
- 获取管道头或者尾的
ChannelHandler
以及ChannelHandlerContext
对象。- 获取管道拥有的
ChannelHandler
名称的列表。- 获取此管道拥有的
ChannelHandler
集合。
1.2.2 继承自 ChannelInboundInvoker
和 ChannelOutboundInvoker
方法
这两个接口与入站事件处理接口
ChannelInboundHandler
和出站事件处理接口ChannelOutboundHandler
息息相关。
ChannelInboundInvoker
是用来发送入站事件的;ChannelOutboundInvoker
是用来发送出站事件的。
在 ChannelPipeline
实现中:
-
ChannelInboundInvoker
发送的入站事件,会直接通过管道管理的ChannelInboundHandler
列表从头到尾的触发,采用拦截器模式,由ChannelInboundHandler
决定是否继续调用下一个ChannelInboundHandler
处理。 -
ChannelOutboundInvoker
发送的出站事件,会直接通过管道管理的ChannelOutboundHandler
列表从尾到头的触发,采用拦截器模式,由ChannelOutboundHandler
决定是否继续调用下一个ChannelOutboundHandler
处理。
1.3 小结
你会发现 ChannelPipeline
的主要功能就两个:
- 储存多个
ChannelHandler
的实例,将它们分为两类,入站事件处理器列表和出站事件处理器列表。- 其实
ChannelPipeline
里面储存的是由ChannelHandler
创建的ChannelHandlerContext
实例。 - 储存的
ChannelHandler
列表是有顺序的,按照添加操作的顺序。
- 其实
- 将
IO
事件使用拦截器的模式通过ChannelHandler
列表。- 对于入站事件是从头到尾地经过
ChannelInboundHandler
列表,由ChannelInboundHandler
决定是否调用列表中下一个ChannelInboundHandler
。 - 对于出站事件是从尾到头地经过
ChannelOutboundHandler
列表,由ChannelOutboundHandler
决定是否调用列表中上一个ChannelOutboundHandler
。
- 对于入站事件是从头到尾地经过
二. ChannelHandlerContext 接口
2.1 介绍
上下文ChannelHandlerContext
接口与 ChannelPipeline
联系非常大
其实它唯一创建的地方,就是当一个
ChannelHandler
添加到管道ChannelPipeline
时,由ChannelPipeline
创建一个包裹ChannelHandler
的上下文ChannelHandlerContext
对象添加进去。
ChannelHandlerContext
接口的重要作用:
-
ChannelHandlerContext
包裹这一个ChannelHandler
,ChannelHandlerContext
必属于一个管道ChannelPipeline
。- 这些都是在
ChannelHandlerContext
创建的时候就绑定的。 - 因为
ChannelHandlerContext
获取到对应的管道,因此动态修改它所属的ChannelPipeline
。
- 这些都是在
-
ChannelHandlerContext
继承ChannelInboundInvoker
和ChannelOutboundInvoker
- 表示
ChannelHandlerContext
也可以发送IO
事件,它可以通知所属的ChannelPipeline
最接近的处理程序器处理IO
事件。 - 对于入站事件,最接近的处理程序器就是当前
ChannelHandler
在管道中下一个入站处理器ChannelInboundHandler
。 - 对于出站事件,最接近的处理程序器就是当前
ChannelHandler
在管道中上一个出站处理器ChannelOutboundHandler
。 - 其实
ChannelPipeline
的拦截器功能,就是通过ChannelHandlerContext
实现的,因为由ChannelHandlerContext
决定是否要调用列表中的下一个处理器处理。
- 表示
- 继承
AttributeMap
接口attr(AttributeKey)
方法允许您存储和访问与ChannelHandler
,Channel
及其上下文相关的有效信息。
2.2 源码
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
/**
* 返回绑定到ChannelHandlerContext的通道Channel。
*/
Channel channel();
/**
* 返回用于执行该上下文 ChannelHandlerContext 方法的执行器 EventExecutor,
* 这个执行器 EventExecutor 可能和 通道Channel的EventExecutor一样,也有可能不一样。
* 如果不一样,说明该上下文中方法的处理在另一个线程,不在通道Channel 的IO线程中处理,不会阻塞通道的IO线程。
*/
EventExecutor executor();
/**
* ChannelHandlerContext的唯一名称。
* 当将ChannelHandler添加到ChannelPipeline时使用该名称。
* 可以使用此名称从ChannelPipeline 获取已注册的ChannelHandler。
*/
String name();
/**
* 绑定此上下文的ChannelHandler对象。
*/
ChannelHandler handler();
/**
* 如果属于此上下文的ChannelHandler已从ChannelPipeline中删除,则返回true。
*
* 注意这个方法只被EventLoop中的内部调用。
*/
boolean isRemoved();
@Override
ChannelHandlerContext fireChannelRegistered();
@Override
ChannelHandlerContext fireChannelUnregistered();
@Override
ChannelHandlerContext fireChannelActive();
@Override
ChannelHandlerContext fireChannelInactive();
@Override
ChannelHandlerContext fireExceptionCaught(Throwable cause);
@Override
ChannelHandlerContext fireUserEventTriggered(Object evt);
@Override
ChannelHandlerContext fireChannelRead(Object msg);
@Override
ChannelHandlerContext fireChannelReadComplete();
@Override
ChannelHandlerContext fireChannelWritabilityChanged();
@Override
ChannelHandlerContext read();
@Override
ChannelHandlerContext flush();
/**
* 此上下文所属的管道`ChannelPipeline`。
*/
ChannelPipeline pipeline();
/**
* Return the assigned {@link ByteBufAllocator} which will be used to allocate {@link ByteBuf}s.
*/
ByteBufAllocator alloc();
/**
* @deprecated Use {@link Channel#attr(AttributeKey)}
*/
@Deprecated
@Override
Attribute attr(AttributeKey key);
/**
* @deprecated Use {@link Channel#hasAttr(AttributeKey)}
*/
@Deprecated
@Override
boolean hasAttr(AttributeKey key);
}
ChannelHandlerContext
的功能大概分为两类:
- 继承自
ChannelInboundInvoker
和ChannelOutboundInvoker
ChannelInboundInvoker
和ChannelOutboundInvoker
将在下一篇文章中介绍。 - 获取属性
-
ChannelHandler handler()
: 绑定此上下文的事件处理器ChannelHandler
。 -
ChannelPipeline pipeline()
: 此上下文所属的管道ChannelPipeline
。 -
EventExecutor executor()
: 此上下文对应的事件处理器ChannelHandler
方法运行所在的线程,有可能是所属通道Channel
的事件轮询器,也有可能是用户额外的事件执行器。 -
String name()
: 该上下文的名字,在所属管道中是唯一的。 -
boolean isRemoved()
: 此上下文从管道中移除时,返回true
。 -
Channel channel()
,ByteBufAllocator alloc()
,attr(AttributeKey
和key) hasAttr(AttributeKey
;都是从管道得到对应的通道key) Channel
,然后从通道中再获取相关属性。
-
三. 总结
-
ChannelPipeline
添加ChannelHandler
时,会创建这个ChannelHandler
的上下文对象ChannelHandlerContext
,再添加到管道中。 - 在
ChannelHandler
中可以得到这个上下文对象ChannelHandlerContext
,这样它就可以:- 可以向上游或下游传递事件,实现责任链的功能,将事件传递给下一个处理器
ChannelHandler
处理。 - 可以动态修改管道
ChannelPipeline
,因为上下文对象可以获取所属的管道。因此它也可以从管道头或者尾重新处理事件。 - 可以通过
attr(AttributeKey
存储特定于该处理器key) ChannelHandler
的信息。
- 可以向上游或下游传递事件,实现责任链的功能,将事件传递给下一个处理器