Netty5源码分析(四) -- 事件分发模型

13 篇文章 3 订阅

所谓Netty5的事件分发模型,主要指的是ChannelPipline, ChannelHandlerContext, ChannelHandlerInvoker, ChannelHandler, Unsafe 这几个核心接口之间的交互模型。


首先是ChannelPipline接口,它是Netty中事件分发的路径。每个Channel都会绑定一个ChannelPipeline来分发事件。

public interface Channel extends AttributeMap, Comparable<Channel> {
ChannelPipeline pipeline();
}

ChannelPipeline是一个大的接口,是一个Facade门面模式的实例。它主要包括三部分的接口,

1. 链表的接口,包括各种遍历,修改链表的操作

2. inbound事件的接口,以fireXXXX开头的方法

3. outbound事件的接口, 不带fire的方法,比如read, write,bind, connect等

inbound,outbound事件是Netty抽象的事件概念,从底层IO事件到用户事件的方向是inbound事件,从用户事件到底层IO事件的方向是outbound事件


DefaultChannelPipeline是ChannelPipeline接口的具体实现,它处理实际的事件分发。它采用了两个单链表head, tail 来处理inbound,outbound事件。

单链表的节点是ChannelHandlerContext,它通过next, prev两个指针指向前后的节点。

head链表的第一个节点是HeadHandler, tail节点的第一个节点是TailHandler。 HeadHandler里面封装了Unsafe接口, 来进行实际的IO读写。inbound事件从底层IO开始,outbound事件到底层IO结束,所以inbound事件链的起点从HeadHandler开始,outbound事件链的终点在HeadHandler结束

在上一篇将Netty如何注册OP_ACCEPT,OP_READ事件时 http://blog.csdn.net/iter_zc/article/details/39396169,我们看到每次Channel注册到Selector时,只设置了Attachment,没有真正的注册,真正的注册是在注册后调用Channel.read,通过HeadHandler的read方法,调用到AbstractChannel的doBeginRead来把OP_ACCEPT,OP_READ设置到SelectionKey的interestOps中

final class DefaultChannelPipeline implements ChannelPipeline {

    final DefaultChannelHandlerContext head;
    final DefaultChannelHandlerContext tail;

    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;
    }

}
final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext { 

   volatile DefaultChannelHandlerContext next;
    volatile DefaultChannelHandlerContext prev;

    private final AbstractChannel channel;
    private final DefaultChannelPipeline pipeline;
    private final String name;
    private final ChannelHandler handler;
    private boolean removed;

    final int skipFlags;

    // Will be set to null if no child executor should be used, otherwise it will be set to the
    // child executor.
    final ChannelHandlerInvoker invoker;
    private ChannelFuture succeededFuture;
}

static final class HeadHandler extends ChannelHandlerAdapter {

        protected final Unsafe unsafe;

        protected HeadHandler(Unsafe unsafe) {
            this.unsafe = unsafe;
        }

}


ChannelHandlerContext,它相当于链表中的节点,从上面的代码中可以看到,它关联了一个ChannelHandler和ChannelHandlerInvoker。

ChannelHandlerInvoker封装了线程池调用ChannelHandler的接口。DefaultChannelHandlerInvoker是默认实现,它关联了一个EventExecutor来实际执行ChannelHandler,这里实际的对象是NioEventLoop,使用单线程来执行一个Channel所有的ChannelHandler。

DefaultChannelHandlerInvoker使用了ChannelHandlerInvokerUtil来调用ChannelHandler的方法。这里我们看到ChannelHandlerInvoker的模型

1. DefaultChannelHandlerInvoker负责选择执行的线程

2. ChannelHandlerInvokerUtil调用ChannelHandler的方法

public class DefaultChannelHandlerInvoker implements ChannelHandlerInvoker {

    private final EventExecutor executor;

    @Override
    public void invokeChannelRegistered(final ChannelHandlerContext ctx) {
        if (executor.inEventLoop()) {
            invokeChannelRegisteredNow(ctx);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    invokeChannelRegisteredNow(ctx);
                }
            });
        }
    }

public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {

    private final ChannelHandlerInvoker invoker = new DefaultChannelHandlerInvoker(this);
}

public final class ChannelHandlerInvokerUtil {

    public static void invokeChannelRegisteredNow(ChannelHandlerContext ctx) {
        try {
            ctx.handler().channelRegistered(ctx);
        } catch (Throwable t) {
            notifyHandlerException(ctx, t);
        }
    }
}

DefaultChannelHandlerContext还保存了一个它关联的ChannelHandler的skipflag信息。skipflag指的是一个ChannelHandler可以使用@Skip注解来表示它不处理某个方法。DefaultChannelHandlerContext记录了这些信息,可以在事件到来的时候直接判断是否要跳过处理。


private static int skipFlags0(Class<? extends ChannelHandler> handlerType) {
        int flags = 0;
        try {
            if (handlerType.getMethod(
                    "handlerAdded", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
                flags |= MASK_HANDLER_ADDED;
            }
            if (handlerType.getMethod(
                    "handlerRemoved", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
                flags |= MASK_HANDLER_REMOVED;
            }
            if (handlerType.getMethod(
                    "exceptionCaught", ChannelHandlerContext.class, Throwable.class).isAnnotationPresent(Skip.class)) {
                flags |= MASK_EXCEPTION_CAUGHT;
            }
            if (handlerType.getMethod(
                    "channelRegistered", ChannelHandlerContext.class).isAnnotationPresent(Skip.class)) {
                flags |= MASK_CHANNEL_REGISTERED;
            }
}


ChannelHandler接口定义了如何处理inbound和outbond事件的方法,是职责链节点的具体实现,也是业务代码编写地方。Netty抽象了Pipeline这个职责链,将底层IO处理和业务处理进行了隔离,使用户可以关注与业务逻辑的实现。


public interface ChannelHandler {

    ///
    // Inbound event handler methods //
    ///

    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

  
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;

    // Outbound event handler methods //
    

    
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

   
    void connect(
            ChannelHandlerContext ctx,
            SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception;
}

Unsafe定义了实际IO处理的接口,它的含义不是说它的方法是不安全的,而是说它的接口是给框架本身调用的,不要暴露给业务层调用。

Unsafe的最底层实现类采用了模板方法模式,NioMessageUnsafe绑定到了NioServerSocketChannel,NioByteUnsafe绑定到NioSocketChannel,

最终的IO读写方法实现在NioServerSocketChannel和NioByteUnsafe中,调用了Java的ServerSocketChannel和SocketChannel来实现。

interface Unsafe {

        ChannelHandlerInvoker invoker();

     SocketAddress localAddress();

        SocketAddress remoteAddress();

        void register(ChannelPromise promise);
    
        void bind(SocketAddress localAddress, ChannelPromise promise);
  
        void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
 
        void disconnect(ChannelPromise promise);

        void close(ChannelPromise promise);

        void closeForcibly();
     
        void beginRead();

        void write(Object msg, ChannelPromise promise);

        void flush();
        
        ChannelPromise voidPromise();

        ChannelOutboundBuffer outboundBuffer();
    }

<pre name="code" class="java">
private final class NioByteUnsafe extends AbstractNioUnsafe {
        private RecvByteBufAllocator.Handle allocHandle;

        @Override
        public void read() {
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final int maxMessagesPerRead = config.getMaxMessagesPerRead();
            RecvByteBufAllocator.Handle allocHandle = this.allocHandle;
            if (allocHandle == null) {
                this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();
            }
            if (!config.isAutoRead()) {
                removeReadOp();
            }

            ByteBuf byteBuf = null;
            int messages = 0;
            boolean close = false;
            try {
                int byteBufCapacity = allocHandle.guess();
                int totalReadAmount = 0;
                do {
                    byteBuf = allocator.ioBuffer(byteBufCapacity);
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);
                    if (localReadAmount <= 0) {
                        // not was read release the buffer
                        byteBuf.release();
                        close = localReadAmount < 0;
                        break;
                    }

                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;

                    if (totalReadAmount >= Integer.MAX_VALUE - localReadAmount) {
                        // Avoid overflow.
                        totalReadAmount = Integer.MAX_VALUE;
                        break;
                    }

                    totalReadAmount += localReadAmount;
                    if (localReadAmount < writable) {
                        // Read less than what the buffer can hold,
                        // which might mean we drained the recv buffer completely.
                        break;
                    }
                } while (++ messages < maxMessagesPerRead);

                pipeline.fireChannelReadComplete();
                allocHandle.record(totalReadAmount);

                if (close) {
                    closeOnRead(pipeline);
                    close = false;
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close);
            }
        }
    }

//NioSocketChannel.doReadBytes()调用SocketChannel来读取数据
 protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
    }

 





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值