On Netty in ChannelPipeline

Netty in two other important components - ChannelHandle, ChannelHandleContext, Pipeline. Netty propagation mechanism in the I / O event and write data filtering and responsible by them.

pipeline initialization

step

  • pipeline is created when you create a Channel
    • AbstractChannel中 pipeline = newChannelPipeline()-DefaultChannelPipeline
  • pipeline node data structure: ChannelHandlerContext doubly linked list
  • pipeline in the two Sentinel: head and tail

analysis

  • pipeline is created when you create a Channel
    • In the server when the client-side Channel and Channel created, call the parent class initialization time AbstractChannel pipeline will be initialized.
    // AbstractChannel(..)
    protected AbstractChannel(Channel parent) {
        ...
        // 创建pipeline
        pipeline = newChannelPipeline();
    }
    // newChannelPipeline()
    protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }
    // DefaultChannelPipeline(..)
    protected DefaultChannelPipeline(Channel channel) {
        ...
        tail = new TailContext(this);
        head = new HeadContext(this);
        head.next = tail;
        tail.prev = head;
    }
复制代码
  • Construction methods and TailContext look HeadContext
    final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
        ...
        HeadContext(DefaultChannelPipeline pipeline) {
            super(pipeline, null, HEAD_NAME, false, true);
        ...    
    }
    final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
        TailContext(DefaultChannelPipeline pipeline) {
            super(pipeline, null, TAIL_NAME, true, false);
            ...
    }
    abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {
      ...
      AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,boolean inbound, boolean outbound) {
          this.name = ObjectUtil.checkNotNull(name, "name");
          this.pipeline = pipeline;
          this.executor = executor;
          this.inbound = inbound;
          this.outbound = outbound;
          ...
      }
复制代码

head and tail thereof will call the parent class constructor AbstractChannelHandlerContext to complete the initialization, whereby we can foresee ChanelPipeline stored inside one ChannelHandlerContext, according to the construction method DefaultChannelPipeline we know that they doubly linked list data structure, according to AbstractChannelHandlerContext construction method, we can found head specified for the stack process, the tail of the stack designated processor.

  • pipeline in the two Sentinel: head and tail

pipeline inside the event propagation mechanism we are going to verify, but we can speculate stack began to spread out from the head, because it is a stack processor, so it's just down the spread without any treatment until the tail will end. Stack began to spread from the tail, because he is a stack processor, so it's just down the propagation of an event can not do anything. So it seems to stack, from the head to the tail end of the beginning; for out of the stack on the contrary, from the beginning to the end of the tail head.

Add Remove ChannelHandler

step

  • Add ChannelHandler process
    • Repeat to determine whether to add
      • filterName(..)
    • Create a node and added to the list
    • Add a callback completion event
      • callHandlerAdded0(..)
  • Delete ChannelHandler process
    • Find the node
    • Delete the list
    • Delete callback event handler

analysis

  • Repeat to determine whether to add
    // filterName(..)
    private String filterName(String name, ChannelHandler handler) {
        if (name == null) {
            return generateName(handler);
        }
        checkDuplicateName(name);
        return name;
    }
    // 判断重名
    private void checkDuplicateName(String name) {
        if (context0(name) != null) {
            throw new IllegalArgumentException("Duplicate handler name: " + name);
        }
    }
    // 找有没有同名的context
    private AbstractChannelHandlerContext context0(String name) {
        AbstractChannelHandlerContext context = head.next;
        while (context != tail) {
            if (context.name().equals(name)) {
                return context;
            }
            context = context.next;
        }
        return null;
    }
复制代码
  • Create a node and added to the list
    // 插入到链表中tail节点的前面。
    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }
复制代码
  • Along callHandlerAdded0 (..) method has been with callHandlerAdded AbstractChannelHandlerContext of (..)
    final void callHandlerAdded() throws Exception {
        ...
        if (setAddComplete()) {
            // 调用具体handler的handlerAdded方法
            handler().handlerAdded(this);
        }
    }
复制代码
  • Delete ChannelHandler process
  • Find the node
    private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
        AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
        if (ctx == null) {
            throw new NoSuchElementException(handler.getClass().getName());
        } else {
            return ctx;
        }
    }
    // 相同堆内地址即为找到
    public final ChannelHandlerContext context(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }
        AbstractChannelHandlerContext ctx = head.next;
        for (;;) {

            if (ctx == null) {
                return null;
            }

            if (ctx.handler() == handler) {
                return ctx;
            }

            ctx = ctx.next;
        }
    }
复制代码
  • Delete the list
    private static void remove0(AbstractChannelHandlerContext ctx) {
        AbstractChannelHandlerContext prev = ctx.prev;
        AbstractChannelHandlerContext next = ctx.next;
        prev.next = next;
        next.prev = prev;
    }
复制代码
  • Callback handler remove method
    final void callHandlerRemoved() throws Exception {
        try {
            // Only call handlerRemoved(...) if we called handlerAdded(...) before.
            if (handlerState == ADD_COMPLETE) {
                handler().handlerRemoved(this);
            }
        } finally {
            // Mark the handler as removed in any case.
            setRemoved();
        }
    }
复制代码

Dissemination and abnormal events

step

  • Spread inBound events
    • Spread ChannelRead events
    • SimpleInBoundHandler processor
  • Spread outBound events
    • Spread write events
  • Anomalous propagation
    • Throw chain

analysis

  • Propagation analysis inBound events
    // 省略代码
    ... 
    serverBootstrap
    ...
    .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new Inbound1())
                                .addLast(new InBound2())
                                .addLast(new Inbound3());
                    }
    });
    ...
    public class Inbound1 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("at InBound1: " + msg);
            ctx.fireChannelRead(msg);
        }
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.channel().pipeline().fireChannelRead("hello cj");
        }
    }
    public class Inbound2 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("at InBound2: " + msg);
            ctx.fireChannelRead(msg);
        }

    }
    public class Inbound3 extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("at InBound3: " + msg);
            ctx.fireChannelRead(msg);
        }
    }
复制代码
  • We now look to draw clients to access when doing push handle, in the case of pipeline Channel Client:

From the beginning has been head down a inboud spread until the end of the tail, you can also see the role played by ChannelHandlerContext it is the middle link, it can get a handle can also get up to the channel and pipeline, a channel will be only one pipeline, a pipeline can have a plurality of the stack and the stack handler handler, and each handler will be ChannelHandlerContext wrapped. Event propagation dependent ChannelHandlerContext of fire * method.

  • Under our code verification to run: telnet client to create a connection
  • Console Print

As we said above that InBoud1 -> InBound2 -> InBoud3

  • Propagation analysis outBound events
public class Outbound1 extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("oubound1 write:" + msg);
        ctx.write(msg, promise);
    }

}

public class Outbound2 extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("oubound2 write:" + msg);
        ctx.write(msg, promise);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        ctx.executor().schedule(()-> {
            ctx.channel().pipeline().write("hello cj...");
        }, 5, TimeUnit.SECONDS);
    }

}

public class Outbound3 extends ChannelOutboundHandlerAdapter {

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        System.out.println("oubound3 write:" + msg);
        ctx.write(msg, promise);
    }

}
复制代码
  • We write to draw about the situation when the time to make a stack processing to the client, pipeline client Channel in now:

Transfer and stack the sequence of events is exactly the opposite, that is, starting from the tail of the list.

  • We verify the results under
  • Anomalous propagation
public class Inbound1 extends ChannelInboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Inbound1...");
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        throw new RuntimeException("cj test throw caught...");
    }
}
public class Inbound3 extends ChannelInboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Inbound2...");
        super.exceptionCaught(ctx, cause);
    }

}
public class Outbound1 extends ChannelOutboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Outbound1...");
        super.exceptionCaught(ctx, cause);
    }

}
public class Outbound2 extends ChannelOutboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Outbound2...");
        super.exceptionCaught(ctx, cause);
    }

}
public class Outbound3 extends ChannelOutboundHandlerAdapter {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("Outbound3...");
        super.exceptionCaught(ctx, cause);
    }

}
复制代码

Abnormal propagation from the head end has been traversed tail, and print it in the tail.

  • Under the direct result of verification
  • For ctx.write () and ctx.pipeline (). Write () and there are different
  ctx.write("hello cj...");
  ctx.pipeline().write("hello cj...");
复制代码

ctx.write (..) We follow the contents above are conceivable, ctx.write actually direct activation of the current one node of a write, so it will not start through all outbound from the tail forward, and ctx.pipeline () .write (..) we can see the source code to know, it first calls the write method pipeline, tracking the source code (below) can be found, he is traversing from the tail, all the outboud will in turn be executed. The same is true inbound

Source Address

github.com/coderjia061…

Guess you like

Origin blog.csdn.net/weixin_34080903/article/details/91386362