Interface ChannelPipeline

官方文档:

Interface ChannelPipeline ------------------ Netty API Reference (4.0.54.Final)

【简称ChannelHandlerContext------------->ctx】
【简称ChannelHandler------------------------>handler】
【简称ChannelPipeline------------------------>pipeline】

A list of ChannelHandlers which handles or intercepts inbound events and outbound operations of a Channel. ChannelPipeline implements an advanced form of the Intercepting Filter pattern to give a user full control over how an event is handled and how the ChannelHandlers in a pipeline interact with each other.

handler负责处理或拦截channel中inbound事件,以及outbound操作。pipeline是一种高大上的拦截过滤器,让用户随心所欲地处理事件,还能操作pipeline中的handler相互通信。

Creation of a pipeline

Each channel has its own pipeline and it is created automatically when a new channel is created.

每个channel都有自己的pipeline。且每当创建一个channel,pipeline就随之自动创建。

How an event flows in a pipeline

The following diagram describes how I/O events are processed by ChannelHandlers in a ChannelPipeline typically. An I/O event is handled by either a ChannelInboundHandler or a ChannelOutboundHandler and be forwarded to its closest handler by calling the event propagation methods defined in ChannelHandlerContext, such as ChannelHandlerContext.fireChannelRead(Object) and ChannelHandlerContext.write(Object).

下图描述了一般情况下pipeline中的handler是怎样处理I/O事件的。一个I/O事件会被inbound handler或outbound handler处理,然后被它们通过特定的方式转发到最近的(或者说“下一个”)handler。特定的转发方式有ctx.fireChannelRead(Object)和ctx.write(Object)。

An inbound event is handled by the inbound handlers in the bottom-up direction as shown on the left side of the diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the diagram. The inbound data is often read from a remote peer via the actual input operation such as SocketChannel.read(ByteBuffer). If an inbound event goes beyond the top inbound handler, it is discarded silently, or logged if it needs your attention.

很好理解,inbound handler负责处理inbound事件,inbound事件来自socket.read()(见上图左半部分最下面)。通常inbound handler就是处理从I/O线程来的inbound数据(见图底部)。inbound数据
往往来自远程设备,我们通过SocketChannel.read(ByteBuffer)读取。如果inbound事件走出最顶部的inbound handler(即图中Inbound Handler N),它会消失在茫茫人海中。当然你也可以在它逃离最顶部handler前获取它的信息。

An outbound event is handled by the outbound handler in the top-down direction as shown on the right side of the diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests. If an outbound event goes beyond the bottom outbound handler, it is handled by an I/O thread associated with the Channel. The I/O thread often performs the actual output operation such as SocketChannel.write(ByteBuffer).

outbound handler自然就是处理outbound事件,outbound事件最终以socket.write()完成(见上图右半部分最下面)。outbound handler通常通过写入请求产生或改变outbound通信量。如果outbound事件走出最底部的outbound handler,那么它会被与当前channel有关的I/O线程处理。此I/O线程会调用SocketChannel.write(ByteBuffer)完成写操作。

For example, let us assume that we created the following pipeline:
ChannelPipeline p = ...;
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());

In the example above, the class whose name starts with Inbound means it is an inbound handler. The class whose name starts with Outbound means it is a outbound handler.
In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound. When an event goes outbound, the order is 5, 4, 3, 2, 1. On top of this principle, ChannelPipeline skips the evaluation of certain handlers to shorten the stack depth:
● 3 and 4 don't implement ChannelInboundHandler, and therefore the actual evaluation order of an inbound event will be: 1, 2, and 5.
● 1 and 2 don't implement ChannelOutboundHandler, and therefore the actual evaluation order of a outbound event will be: 5, 4, and 3.
● If 5 implements both ChannelInboundHandler and ChannelOutboundHandler, the evaluation order of an inbound and a outbound event could be 125 and 543 respectively.

上例中,以Inbound开头的类名是inbound handler,以outbound开头的类名是outbound handler。
根据上面的设置,当一个inbound事件到来,handler的执行顺序是1,2,3,4,5。而一个outbound事件到来,handler的执行顺序是5,4,3,2,1。
根据这个规则,pipeline会一些handler来缩短“堆深度”。

  • 因为3号和4号handler没有实现ChannelInboundHandler,所以当一个inbound事件到来,实际上执行顺序是1,2,5
  • 因为1号和2号handler没有实现ChannelOutboundHandler,所以当一个outbound事件到来,实际上执行顺序是5,4,3
  • 如果5号handler实现了ChannelInboundHandler和ChannelOutboundHandler那么,当inbound或outbound事件到来时,执行顺序会是1,2,5或5,4,3(其实就如上两条所述)

Forwarding an event to the next handler

As you might noticed in the diagram shows, a handler has to invoke the event propagation methods in ChannelHandlerContext to forward an event to its next handler. Those methods include:



你可能在上图已经看出来,一个handler必须靠ctx调用事件传递方法把事件传递到下一个handler,这些方法包括:

● Inbound event propagation methods:
○ ChannelHandlerContext.fireChannelRegistered()
○ ChannelHandlerContext.fireChannelActive()
○ ChannelHandlerContext.fireChannelRead(Object)
○ ChannelHandlerContext.fireChannelReadComplete()
○ ChannelHandlerContext.fireExceptionCaught(Throwable)
○ ChannelHandlerContext.fireUserEventTriggered(Object)
○ ChannelHandlerContext.fireChannelWritabilityChanged()
○ ChannelHandlerContext.fireChannelInactive()
○ ChannelHandlerContext.fireChannelUnregistered()
● Outbound event propagation methods:
○ ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
○ ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
○ ChannelHandlerContext.write(Object, ChannelPromise)
○ ChannelHandlerContext.flush()
○ ChannelHandlerContext.read()
○ ChannelHandlerContext.disconnect(ChannelPromise)
○ ChannelHandlerContext.close(ChannelPromise)
○ ChannelHandlerContext.deregister(ChannelPromise)

and the following example shows how the event propagation is usually done:
下面的例子说明handler之间是如何传递事件的

 public class MyInboundHandler extends ChannelInboundHandlerAdapter {
      @Override
     public void channelActive(ChannelHandlerContext ctx) {
         System.out.println("Connected!");
         ctx.fireChannelActive();
     }
 }

 public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
      @Override
     public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
         System.out.println("Closing ..");
         ctx.close(promise);
     }
 }

Building a pipeline

A user is supposed to have one or more ChannelHandlers in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:

  1. Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object.
  2. Protocol Encoder - translates a Java object into binary data.
  3. Business Logic Handler - performs the actual business logic (e.g. database access).
    and it could be represented as shown in the following example:

用户在pipeline上应该有多条handler来接收I/O事件(如,读)和请求I/O操作(如,写和关闭操作)。比如,一个服务器在每个pipeline中会有如下handler,但这还是要根据你的情况而定,比如复杂度和协议的特性,以及业务逻辑。

  1. 协议解码器 - 将二进制数据(如ByteBuf)转化为Java对象
  2. 协议编码器 - 将Java对象转换为二进制数据
  3. 业务逻辑handler - 实现真正的业务逻辑(如数据库访问)

一个典型的例子如下:

 static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
 ...

 ChannelPipeline pipeline = ch.pipeline();

 pipeline.addLast("decoder", new MyProtocolDecoder());
 pipeline.addLast("encoder", new MyProtocolEncoder());

 // Tell the pipeline to run MyBusinessLogicHandler's event handler methods
 // in a different thread than an I/O thread so that the I/O thread is not blocked by
 // a time-consuming task.
 // If your business logic is fully asynchronous or finished very quickly, you don't
 // need to specify a group.


//告知pipline在另一个线程(非I/O线程)运行MyBusinessLogicHandler的事件处理方法,
//这样可以防止I/O线程被耗时任务阻塞。如果你的业务逻辑是完全异步的,
//或者说完成得非常快,那么你不必指定一个组执行handler。

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

Thread safety

A ChannelHandler can be added or removed at any time because a ChannelPipeline is thread safe. For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it after the exchange.

handler可以随时添加或移除,因为pipeline是线程安全的。举个例子:当你正准备传递一些敏感数据时,可以临时添加一个加密handler,在数据传递完成后把它删除掉。

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

推荐阅读更多精彩内容

  • 2015,做了一些不痛不痒的事,开始的热情是有的,只是做到了后面却反而越做越想放弃,即便是坚持完成了,也没有取...
    跌落的年华褪去的疤阅读 377评论 0 1
  • ———落花有意 流水无情 暗恋是一池心事,他侧身走过的微风就能吹起你心中的朵朵涟漪。 小A来公司三个月了,这三个月...
    芊芊君紫阅读 600评论 2 2
  • 问题的源头就是设计时候留下的,重要的事不怕画时间,精力,等 一路向前,就会有幸运之星等着你
    阳光创客敖伟伟阅读 146评论 0 0