Handler、Pipeline

ChannelHandler

public interface ChannelHandler

1、处理一个 I/O 事件或拦截一个 I/O 操作,并将其转发给其 ChannelPipeline 中的下一个 handler

2、ChannelHandler 本身并没有提供很多方法,但通常要实现它的一个子类型

(1)ChannelInboundHandler 处理入站 I/O 事件,而 ChannelOutboundHandler 处理出站 I/O 操作

(2)另外为了方便,还提供以下适配器类:ChannelInboundHandlerAdapter 处理入站 I/O 事件,ChannelOutboundHandlerAdapter 处理出站I/O操作,ChannelDuplexHandler 处理入站和出站事件

3、分为入站、出站

(1)入站处理器通常是 ChannelInboundHandlerAdapter 的子类,主要用来读取客户端数据,写回结果

(2)出站处理器通常是 ChannelOutboundHandlerAdapter 的子类,主要对写回结果进行加工

4、上下文对象

(1)一个 ChannelHandler 被提供给一个 ChannelHandlerContext 对象

(2)ChannelHandler 应该通过上下文对象,与它所属的 ChannelPipeline 交互

(3)使用上下文对象,ChannelHandler 可以向上游或下游传递事件,动态地修改管道,或存储处理程序的特定信息(使用 AttributeKeys)

5、状态管理

(1)ChannelHandler 经常需要存储一些有状态的信息,最简单和推荐的方法是使用成员变量

(2)因为 handler 实例有一个专用于一个连接的状态变量,所以必须为每个新的 channel 创建一个新的 handler 实例,以避免出现未认证的客户可以获得机密信息的竞争条件

6、使用 AttributeKeys

(1)尽管建议使用成员变量来存储处理程序的状态,但由于某些原因,可能不想创建许多 handler 实例

(2)在这种情况下,可以使用由 ChannelHandlerContext 提供的 AttributeKeys

(3)handler 的状态被附加到 ChannelHandlerContext 上,可以将同一个 handler 实例添加到不同的 pipeline 中

7、@Sharable 注解

(1)如果一个 ChannelHandler 使用 @Sharable 注解,这意味着可以只创建一个 handler 实例,并多次将其添加到一个或多个 ChannelPipeline 中,而不会出现竞争状况

(2)如果没有指定这个注解,必须在每次将其添加到 pipeline 中时,创建一个新的 handler,因为它有不共享的状态,如成员变量

(3)这个注解是为文档提供的,就像 JCIP 的注解一样

 

ChannelPipeline

public interface ChannelPipeline
extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Map.Entry<String,ChannelHandler>>

1、多个 ChannelHandler 组成的一个列表,它处理或拦截一个 Channel 的入站事件和出站操作

2、ChannelPipeline 实现拦截过滤器模式的高级形式,让用户完全控制事件的处理方式,以及管道中的 多个 ChannelHandler 如何相互作用

3、每个 Channel 都有自己的 ChannelPipeline,当一个新的 Channel 被创建时,ChannelPipeline 会被自动创建

4、事件如何在 ChannelPipeline 中流动

(1)下图描述 I/O 事件是如何被 ChannelPipeline 中的 ChannelHandler 处理

(2)一个 I/O 事件由 ChannelInboundHandler 或 ChannelOutboundHandler 处理,并通过调用ChannelHandlerContext 中定义的事件传播方法,如:ChannelHandlerContext.fireChannelRead(Object) 和 ChannelOutboundInvoker.write(Object),转发给其最近的 handler

                                                 I/O Request
                                            via Channel or
                                        ChannelHandlerContext
                                                      |
  +---------------------------------------------------+---------------+
  |                           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)            |
  +-------------------------------------------------------------------+

(3)一个入站事件由入站处理程序自下而上地处理,如图左侧所示

(4)入站处理程序通常处理由图中底部的 I/O 线程产生的入站数据

(5)入站数据通常是通过实际的输入操作,如:SocketChannel.read(ByteBuffer)

(6)如果一个入站事件超出顶部的入站处理程序,它就会被静默地丢弃,如果需要被关注,则会被记录下来

(7)一个出站事件被出站处理程序以自上而下的方式处理,如图右侧所示

(8)出站处理程序通常生成或转发出站流量,如写请求

(9)如果一个出站事件超出底部的出站处理程序,它就会被一个与通道相关的 I/O 线程处理

(10)I/O 线程通常执行实际的输出操作,如:SocketChannel.write(ByteBuffer)

5、将一个事件转发到下一个 handler

(1)一个 handler 必须调用 ChannelHandlerContext 中的事件传播方法,转发一个事件到它的下一个 handler

(2)入站事件传播方法

ChannelHandlerContext.fireChannelRegistered()
ChannelHandlerContext.fireChannelActive()
ChannelHandlerContext.fireChannelRead(Object)
ChannelHandlerContext.fireChannelReadComplete()
ChannelHandlerContext.fireExceptionCaught(Throwable)
ChannelHandlerContext.fireUserEventTriggered(Object)
ChannelHandlerContext.fireChannelWritabilityChanged()
ChannelHandlerContext.fireChannelInactive()
ChannelHandlerContext.fireChannelUnregistered()

(3)出站事件传播方法

ChannelOutboundInvoker.bind(SocketAddress, ChannelPromise)
ChannelOutboundInvoker.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelOutboundInvoker.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelOutboundInvoker.disconnect(ChannelPromise)
ChannelOutboundInvoker.close(ChannelPromise)
ChannelOutboundInvoker.deregister(ChannelPromise)

6、构建一个 ChannelPipeline

(1)用户应该在 ChannelPipeline 中设置一个或多个 ChannelHandler 来接收 I/O 事件(如:读取)和请求 I/O 操作(如:写入、关闭)

(2)注意:虽然使用 DefaultEventLoopGroup 将从 EventLoop 中卸载操作,但它仍将以每个 ChannelHandlerContext 的串行方式处理任务,因此保证排序

(3)由于排序,它仍然可能成为一个瓶颈,如果排序不是必要,可能要考虑使用 UnorderedThreadPoolEventExecutor 来最大化任务执行的并行性

7、线程安全

(1)ChannelHandler 可以在任何时候被添加或删除,因为 ChannelPipeline 是线程安全的

(2)例如,当敏感信息即将被交换时,可以插入一个加密 handler,并在交换后删除它

 

EmbeddedChannel

public class EmbeddedChannel
extends AbstractChannel

1、以嵌入式方式使用的通道实现的基类

2、测试各个 handler,通过其构造函数按顺序传入需要测试的 handler,然后调用对应 Inbound 和 Outbound 方法即可

 

事项

1、ChannelPipeLine 添加 handler 时,给 handler 添加 name 参数,这样可以调用 ChannelPipeLine 的 addAfter、addBefore 等方法,更灵活地向 ChannelPipeLine 中添加 handler

2、handler 需要放入 ChannelPipeLine 中,才能根据放入顺序来使用 handler

3、ChannelPipeLine 结构:一个有 head 与 tail 指针的双向链表,其中节点为 handler

final HeadContext head;
final TailContext tail;

4、将当前 handler 的处理结果传递给下一个 handler

(1)当有入站(Inbound)操作时,从 head 到 tail,向后调用 handler,跳过 ChannelOutboundHandler,直到 handler 不处理 Inbound 操作为止

(2)当有出站(Outbound)操作时,从 tail 到 head,向前调用 handler,跳过 ChannelInboundHandler,直到 handler 不处理 Outbound 操作为止

5、不同对象的写操作

(1)Channel 的写操作:从 tail 到 head,处理 Outbound 操作

(2)ChannelHandlerContext 的写操作:从当前 Context 到 head,处理 Outbound 操作

posted @   半条咸鱼  阅读(59)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示