Netty学习:ChannelPipeline

Netty学习:ChannelPipeline_第1张图片

一个{@link ChannelHandler}的列表,它处理或拦截{@link Channel}的入站事件和出站操作。

创建管道

 每个通道都有自己的管道,在创建新通道时自动创建管道。

事件如何在管道中流动

下图描述了在{@link ChannelPipeline}中{@link ChannelHandler}s如何处理I/O事件。I/O事件由{@link ChannelInboundHandler}或{@link ChannelOutboundHandler}处理,并通过调用{@link ChannelHandlerContext}中定义的事件传播方法(例如{@link ChannelHandlerContext#fireChannelRead(Object)}和{@link ChannelHandlerContext#write(Object)})转发到最近的处理程序。

 *                                                 I/O Request
 *                                            via {@link Channel} or
 *                                        {@link 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)            |
 *  +-------------------------------------------------------------------+

入站事件由入站处理程序按照自底向上的方向处理,如图左侧所示。入站处理程序通常处理由图底部的I/O线程生成的入站数据。入站数据通常通过实际输入操作从远程对等点读取,例如{@link SocketChannel#read(ByteBuffer)}。如果入站事件超出了顶级入站处理程序,则会悄无声息地丢弃该事件,或者在需要您注意时记录该事件。

出站事件由出站处理程序按照自顶向下的方向处理,如图右侧所示。出站处理程序通常生成或转换出站通信流,如写请求。如果出站事件超出底部出站处理程序,则由与 {@link Channel}关联的I/O线程处理。I/O线程通常执行实际的输出操作,例如{@link SocketChannel#write(ByteBuffer)}。

例如,假设我们创建了以下管道:

 * p.addLast("1", new InboundHandlerA());
 * p.addLast("2", new InboundHandlerB());
 * p.addLast("3", new OutboundHandlerA());
 * p.addLast("4", new OutboundHandlerB());
 * p.addLast("5", new InboundOutboundHandlerX());

在上面的示例中,名称以{@code Inbound}开头的类意味着它是入站处理程序。名称以{@code Outbound}开头的类意味着它是一个出站处理程序。

在给定的示例配置中,当事件进入入站时,处理程序的评估顺序是1、2、3、4、5。当事件出站时,顺序是5,4,3,2,1。在此原则之上,{@link ChannelPipeline}跳过了对某些处理程序的评估,以缩短堆栈深度:

  • 3和4没有实现{@link ChannelInboundHandler},因此入站事件的实际计算顺序是:1、2和5。
  • 1和2没有实现{@link ChannelOutboundHandler},因此出站事件的实际计算顺序是:5、4和3。
  • 如果5同时实现{@link ChannelInboundHandler}和{@link ChannelOutboundHandler},则入站和出站事件的计算顺序分别为125和543。

将事件转发到下一个处理程序

正如您在图中可能注意到的,处理程序必须调用{@link ChannelHandlerContext}中的事件传播方法,以便将事件转发给下一个处理程序。这些方法包括:

入站事件传播方法:

  • {@link ChannelHandlerContext#fireChannelRegistered()}
  • {@link ChannelHandlerContext#fireChannelActive()}
  • {@link ChannelHandlerContext#fireChannelRead(Object)}
  • {@link ChannelHandlerContext#fireChannelReadComplete()}
  • {@link ChannelHandlerContext#fireExceptionCaught(Throwable)}
  • {@link ChannelHandlerContext#fireUserEventTriggered(Object)}
  • {@link ChannelHandlerContext#fireChannelWritabilityChanged()}
  • {@link ChannelHandlerContext#fireChannelInactive()}
  • {@link ChannelHandlerContext#fireChannelUnregistered()}

 出站事件传播方法:

  • {@link ChannelHandlerContext#bind(SocketAddress, ChannelPromise)}
  • {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)}
  • {@link ChannelHandlerContext#write(Object, ChannelPromise)}
  • {@link ChannelHandlerContext#flush()}
  • {@link ChannelHandlerContext#read()}
  • {@link ChannelHandlerContext#disconnect(ChannelPromise)}
  • {@link ChannelHandlerContext#close(ChannelPromise)}
  • {@link ChannelHandlerContext#deregister(ChannelPromise)}

下面的例子展示了事件传播通常是如何完成的: 

 * public class MyInboundHandler extends {@link ChannelInboundHandlerAdapter} {
 *     {@code @Override}
 *     public void channelActive({@link ChannelHandlerContext} ctx) {
 *         System.out.println("Connected!");
 *         ctx.fireChannelActive();
 *     }
 * }
 *
 * public class MyOutboundHandler extends {@link ChannelOutboundHandlerAdapter} {
 *     {@code @Override}
 *     public void close({@link ChannelHandlerContext} ctx, {@link ChannelPromise} promise) {
 *         System.out.println("Closing ..");
 *         ctx.close(promise);
 *     }
 * }

建立一个管道

用户应该在管道中有一个或多个{@link ChannelHandler}来接收I/O事件(例如读取)和请求I/O操作(例如写入和关闭)。例如,一个典型的服务器在每个通道的管道中都有以下处理程序,但是根据协议和业务逻辑的复杂性和特征,您的里程可能会有所不同:

  • 协议解码器——将二进制数据(例如{@link ByteBuf})转换为Java对象。
  • 协议编码器——将Java对象转换为二进制数据。
  • 业务逻辑Handler——执行实际的业务逻辑(例如数据库访问)。

可以表示为,如下例所示:

  * static final {@link EventExecutorGroup} group = new {@link DefaultEventExecutorGroup}(16);
 * ...
 *
 * {@link ChannelPipeline} pipeline = ch.pipeline();
 *
 * pipeline.addLast("decoder", new MyProtocolDecoder());
 * pipeline.addLast("encoder", new MyProtocolEncoder());

//告诉管道在与I/O线程不同的线程中运行MyBusinessLogicHandler的事件处理程序方法,这样I/O线程就不会被耗时的任务阻塞。

//如果您的业务逻辑是完全异步的,或者非常快地完成,则不需要指定组。

* pipeline.addLast(group, "handler", new MyBusinessLogicHandler());

线程安全

可以在任何时候添加或删除{@link ChannelHandler},因为{@link ChannelPipeline}是线程安全的。例如,您可以在即将交换敏感信息时插入加密处理程序,并在交换后删除它。

ChannelPipeline addFirst(String name, ChannelHandler handler);在管道的第一个位置插入一个{@link ChannelHandler}。

ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);在管道的第一个位置插入一个{@link ChannelHandler}。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline addLast(String name, ChannelHandler handler);在管道的最后一个位置追加一个{@link ChannelHandler}。

 ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);在管道的最后一个位置追加一个{@link ChannelHandler}。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);在该管道的现有handler之前插入{@link ChannelHandler}。

ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);在该管道的现有handler之前插入{@link ChannelHandler}。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);在该管道的现有处理程序之后插入{@link ChannelHandler}。

ChannelPipeline addAfter(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);在该管道的现有处理程序之后插入{@link ChannelHandler}。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline addFirst(ChannelHandler... handlers);在管道的第一个位置插入{@link ChannelHandler}s。

ChannelPipeline addFirst(EventExecutorGroup group, ChannelHandler... handlers);在管道的第一个位置插入{@link ChannelHandler}s。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline addLast(ChannelHandler... handlers);在管道的最后一个位置插入{@link ChannelHandler}s。

ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler... handlers);在管道的最后一个位置插入{@link ChannelHandler}s。{@link EventExecutorGroup}将用于执行{@link ChannelHandler}方法

ChannelPipeline remove(ChannelHandler handler);从该管道中删除指定的{@link ChannelHandler}。

ChannelHandler remove(String name);从管道中删除具有指定名称的{@link ChannelHandler}。

T remove(Class handlerType);从管道中删除指定类型的{@link ChannelHandler}。

ChannelHandler removeFirst();删除管道中的第一个{@link ChannelHandler}。

ChannelHandler removeLast();删除此管道中的最后一个{@link ChannelHandler}。

ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);用管道中的newHandler替换指定的{@link ChannelHandler}。

ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);用管道中的新处理程序替换指定名称的{@link ChannelHandler}。

T replace(Class oldHandlerType, String newName,
                                         ChannelHandler newHandler);用管道中的新处理程序替换指定类型的{@link ChannelHandler}。

ChannelHandler first();返回此管道中的第一个{@link ChannelHandler}。

ChannelHandlerContext firstContext();返回管道中第一个{@link ChannelHandler}的上下文。

ChannelHandler last();返回此管道中的最后一个{@link ChannelHandler}。

ChannelHandlerContext lastContext();返回此管道中最后一个{@link ChannelHandler}的上下文。

ChannelHandler get(String name);返回管道中指定名称的{@link ChannelHandler}。

T get(Class handlerType);返回管道中指定类型的{@link ChannelHandler}。

ChannelHandlerContext context(ChannelHandler handler);返回此管道中指定的{@link ChannelHandler}的上下文对象。

ChannelHandlerContext context(String name);返回管道中指定名称的{@link ChannelHandler}的上下文对象。

ChannelHandlerContext context(Class handlerType);返回管道中指定类型的{@link ChannelHandler}的上下文对象。

Channel channel();返回管道所连接的 {@link Channel}

List names();返回handler的名称LIST

Map toMap();将此管道转换为有序的{@link映射},其键为处理程序名称,其值为处理程序。

 

 

 

 

你可能感兴趣的:(Netty)