【Netty】(8)---ChannelPipeline观点明白_玖富娱乐主管


玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。

ChannelPipeline

     ChannelPipeline不是零丁存在,它肯定会和Channel、ChannelHandler、ChannelHandlerContext联系干系在一同,以是有关观点这里一同讲。

一、ChannelHandler

  1、观点

 先看图

ChannelHandler下主若是两个子接口

      ChannelInboundHandler(入站): 处置惩罚输入数据和Channel状况范例转变。

                                      适配器: ChannelInboundHandlerAdapter(适配器设想形式)

                                      经常运用的: SimpleChannelInboundHandler

    ChannelOutboundHandler(出站): 处置惩罚输出数据

                                      适配器: ChannelOutboundHandlerAdapter

每一个Handler都肯定会处置惩罚出站或许入站(能够两者都处置惩罚数据),比方关于入站的Handler能够会继承SimpleChannelInboundHandler或许ChannelInboundHandlerAdapter,

而SimpleChannelInboundHandler又是继承于ChannelInboundHandlerAdapter,最大的区分在于SimpleChannelInboundHandler会对没有外界援用的资本举行肯定的清算,

并且入站的音讯能够经由过程泛型来划定。

这里为何有设配器形式呢?

      我们在写自定义Handel时刻,很少会直接完成上面两个接口,由于接口中有很多默许要领须要完成,以是这里就采用了设配器形式,ChannelInboundHandlerAdapter和

ChannelInboundHandlerAdapter就是设配器形式的产品,让它去完成上面接口,完成它一切要领。那末你本身写自定义Handel时,只需继承它,就不必重写上面接口的一切要领了。

 2、Channel 生命周期(实行递次也是从上倒下)

   (1)channelRegistered: channel注册到一个EventLoop。

   (2)channelActive: 变成活泼状况(衔接到了长途主机),能够吸收和发送数据

   (3)channelInactive: channel处于非活泼状况,没有衔接到长途主机

   (4)channelUnregistered: channel已竖立,然则未注册到一个EventLoop内里,也就是没有和Selector绑定

 3、ChannelHandler 生命周期

      handlerAdded: 当 ChannelHandler 增加到 ChannelPipeline 挪用

  handlerRemoved: 当 ChannelHandler 从 ChannelPipeline 移除时挪用

 exceptionCaught: 当 ChannelPipeline 实行抛出非常时挪用

 

二、ChannelPipeline

  1、观点

先看图

       ChannelPipeline类是ChannelHandler实例工具的链表,用于处置惩罚或截获通道的吸收和发送数据。它供应了一种高等的截取过滤形式(相似serverlet中的filter功用),让用

户能够在ChannelPipeline中完整掌握一个事宜和怎样处置惩罚ChannelHandler与ChannelPipeline的交互。

       关于每一个新的通道Channel,都邑竖立一个新的ChannelPipeline,并将器pipeline附加到channel中。

下图形貌ChannelHandler与pipeline中的干系,一个io操纵能够由一个ChannelInboundHandler或ChannelOutboundHandle举行处置惩罚,并经由过程挪用ChannelInboundHandler

处置惩罚入站io或经由过程ChannelOutboundHandler处置惩罚出站IO。

2、经常运用要领

      addFirst(...)   //增加ChannelHandler在ChannelPipeline的第一个地位
     addBefore(...)   //在ChannelPipeline中指定的ChannelHandler称号之前增加ChannelHandler
      addAfter(...)   //在ChannelPipeline中指定的ChannelHandler称号以后增加ChannelHandler
       addLast(...)   //在ChannelPipeline的末端增加ChannelHandler
        remove(...)   //删除ChannelPipeline中指定的ChannelHandler
       replace(...)   //替代ChannelPipeline中指定的ChannelHandler

ChannelPipeline能够动态增加、删除、替代个中的ChannelHandler,如许的机制能够进步灵活性。示例:

1.    ChannelPipeline pipeline = ch.pipeline(); 
2.    FirstHandler firstHandler = new FirstHandler(); 
3.    pipeline.addLast("handler1", firstHandler); 
4.    pipeline.addFirst("handler2", new SecondHandler()); 
5.    pipeline.addLast("handler3", new ThirdHandler()); 
6.    pipeline.remove("“handler3“"); 
7.    pipeline.remove(firstHandler); 
8.    pipeline.replace("handler2", "handler4", new FourthHandler());<span style="font-family:Microsoft YaHei;font-size:14px;">

 3、入站出站Handler实行递次

  一样平常的项目中,inboundHandler和outboundHandler有多个,在Pipeline中的实行递次?

   重点记着: InboundHandler递次实行OutboundHandler逆序实行

   题目: 下面的handel的实行递次?

          ch.pipeline().addLast(new InboundHandler1());
          ch.pipeline().addLast(new OutboundHandler1());
          ch.pipeline().addLast(new OutboundHandler2());
          ch.pipeline().addLast(new InboundHandler2());
  或许:
          ch.pipeline().addLast(new OutboundHandler1());
          ch.pipeline().addLast(new OutboundHandler2());
          ch.pipeline().addLast(new InboundHandler1());
          ch.pipeline().addLast(new InboundHandler2());

实在上面的实行递次都是一样的:

 InboundHandler1--> InboundHandler2 -->OutboundHandler2 -->OutboundHandler1

结论

      1)InboundHandler递次实行,OutboundHandler逆序实行

      2)InboundHandler之间通报数据,经由过程ctx.fireChannelRead(msg)

      3)InboundHandler经由过程ctx.write(msg),则会通报到outboundHandler

      4)  运用ctx.write(msg)通报音讯,Inbound须要放在末端,在Outbound以后,否则outboundhandler会不实行;

           然则运用channel.write(msg)、pipline.write(msg)状况会不一致,都邑实行,那是由于channel和pipline会贯串全部流。

      5)  outBound和Inbound谁先实行,针对客户端和服务端而言,客户端是提议要求再吸收数据,先outbound再inbound,服务端则相反

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。-

 

三、ChannelHandlerContext

    ChannelPipeline并非直接治理ChannelHandler,而是经由过程ChannelHandlerContext来间接治理,这一点经由过程ChannelPipeline的默许完成DefaultChannelPipeline能够看出来。

    DefaultChannelHandlerContext和DefaultChannelPipeline是ChannelHandlerContext和ChannelPipeline的默许完成在DefaultPipeline内部

DefaultChannelHandlerContext组成了一个双向链表。 我们看下DefaultChannelPipeline的组织函数:

/**
  * 能够看到,DefaultChinnelPipeline 内部运用了两个特别的Hander 来透露表现Handel链的头和尾。
  */
 public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;
 
        TailHandler tailHandler = new TailHandler();
        tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);
 
        HeadHandler headHandler = new HeadHandler(channel.unsafe());
        head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
 
        head.next = tail;
        tail.prev = head;
    } 

以是关于DefaultChinnelPipeline它的Handel头部和尾部的Handel是流动的,我们所增加的Handel是增加在这个头和尾之前的Handel。(下面这个图越发清楚)

 

四、几者干系

先大抵说下甚么是Channel

一般来讲, 一切的 NIO 的 I/O 操纵都是从 Channel 最先的. 一个 channel 相似于一个 stream。在Netty中,Channel是客户端和服务端竖立的一个衔接通道

虽然java Stream 和 NIO Channel都是卖力I/O操纵,但他们照样有很多区分的:

      1)我们能够在同一个 Channel 中实行读和写操纵, 但是同一个 Stream 仅仅支撑读或写

     2)Channel 能够异步地读写, 而 Stream 是壅塞的同步读写

     3)Channel 老是从 Buffer 中读取数据, 或将数据写入到 Buffer 中

 几者的干系图以下:

总结:

      一个Channel包罗一个ChannelPipeline,竖立Channel时会自动竖立一个ChannelPipeline,每一个Channel都有一个治理它的pipeline,这联系干系是永久性的。

这点从源码中就能够看出,我之前写的博客里有说到:【Netty】5 源码 Bootstrap。每一个ChannelPipeline中能够包罗多个ChannelHandler。一切ChannelHandler

都邑递次加入到ChannelPipeline中,ChannelHandler实例与ChannelPipeline之间的桥梁是ChannelHandlerContext实例。

 

五、全部流传流程

     如今将上面的全部流传流程,经由过程源码大抵走一遍。

     为了搞清楚事宜怎样在Pipeline里流传,让我们从Channel的笼统子类AbstractChannel最先,下面是AbstractChannel#write()要领的完成:

public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
    // ...
    @Override
    public Channel write(Object msg) {
        return pipeline.write(msg);
    }
    // ...
}

     AbstractChannel直接挪用了Pipeline的write()要领:

再看DefaultChannelPipeline的write()要领完成:

final class DefaultChannelPipeline implements ChannelPipeline {
    // ...
    @Override
    public ChannelFuture write(Object msg) {
        return tail.write(msg);
    }
    // ...
}

由于write是个outbound事宜,以是DefaultChannelPipeline直接找到tail局部的context,挪用其write()要领:

 

接着看DefaultChannelHandlerContext的write()要领

final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext {
    // ...
    @Override
    public ChannelFuture write(Object msg) {
        return write(msg, newPromise());
    }
 
    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }
 
        validatePromise(promise, true);
 
        write(msg, false, promise);
 
        return promise;
    }
 
    private void write(Object msg, boolean flush, ChannelPromise promise) {
        DefaultChannelHandlerContext next = findContextOutbound();
        next.invokeWrite(msg, promise);
        if (flush) {
            next.invokeFlush();
        }
    }
 
    private DefaultChannelHandlerContext findContextOutbound() {
        DefaultChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }
 
    private void invokeWrite(Object msg, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }
 
    // ...
}

context的write()要领沿着context链往前找,直至找到一个outbound范例的context为止,然后挪用其invokeWrite()要领

 

  invokeWrite()接着挪用handler的write()要领

 

末了看看ChannelOutboundHandlerAdapter的write()要领完成:

public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {
    // ...
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ctx.write(msg, promise);
    }
    // ...
}

 默许的完成挪用了context的write()要领而不做任何处置惩罚,如许write事宜就沿着outbound链继承流传:

 

可见,Pipeline的事宜流传,是靠Pipeline,Context和Handler配合合作完成的。

 

参考 

   Netty4进修条记-- ChannelPipeline(非常感谢作者分享,让我对事宜流传有了更清楚的熟悉)

 

 

若是一个人充溢快活,正面的头脑,那末好的人事物就会和他共识,并且被他吸收过去。一样,一个人老带伤心,倒运的事变也会跟过去。

                                                                                    ——在本身心境降低的时刻,申饬本身不要把负能量带给他人。(大校18) 

 

-玖富娱乐是一家为代理招商,直属主管信息发布为主的资讯网站,同时也兼顾玖富娱乐代理注册登录地址。