美文网首页程序猿之路
Netty源码分析(四)Unsafe

Netty源码分析(四)Unsafe

作者: 三斤牛肉 | 来源:发表于2017-05-27 11:59 被阅读264次

    这里的Unsafe并不是我们常说的Java自带的sun.misc.Unsafe,而是io.netty.channel.Channel#Unsafe。为什么叫Unsafe呢,按Doc上说法是“Unsafe函数不允许被用户代码使用,这些函数是真正用于数据传输操作,必须被IO线程调用”。 也就是说真正依赖于底层协议/方案的实现是通过Unsafe包装出去的。
    我们先看下Unsafe定义了哪些接口:

    image2.png

    是不是很熟悉
    regisiter bind connect write disconnect close flush beginRead
    这些都是IO操作中经常用到的接口。
    我们再来看一个具体实现,上一节中我们用了NioServerSocketChannel,这节我们就用其父类AbstractNioMessageChannel中定义的NioMessageUnsafe
    先看下该类的继承情况:

    image3.png

    先看下regisiter时候的流程

    @Override
    public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    
      //省略部分代码
      AbstractChannel.this.eventLoop = eventLoop;
      if (eventLoop.inEventLoop()) {
        register0(promise);
      } else {
        try {
          eventLoop.execute(new Runnable() {
            @Override
            public void run() {
              register0(promise);
            }
          });
        } catch (Throwable t) {
          //...      
        }
      }
    }
    
    private void register0(ChannelPromise promise) {
        try {
            // check if the channel is still open as it could be closed in the mean time when the register
            // call was outside of the eventLoop
            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }
            boolean firstRegistration = neverRegistered;
            doRegister();
            neverRegistered = false;
            registered = true;
    
             // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
            // user may already fire events through the pipeline in the ChannelFutureListener.
            pipeline.invokeHandlerAddedIfNeeded();
    
            safeSetSuccess(promise);
            pipeline.fireChannelRegistered(); //触发channelRegisitered事件
            // Only fire a channelActive if the channel has never been registered. This prevents firing
          // multiple channel actives if the channel is deregistered and re-registered.
          if (isActive()) {
            if (firstRegistration) {
                pipeline.fireChannelActive();//触发channelAvtive事件
            } else if (config().isAutoRead()) {
                // This channel was registered before and autoRead() is set. This means we need to begin read
              // again so that we process inbound data.
              // See https://github.com/netty/netty/issues/4805
              beginRead();
            }
        }
      } catch (Throwable t) {
        // Close the channel directly to avoid FD leak.
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
      }
    }
    

    AbstractNioChannel.doRegister():
    在JDK的SelectableChannel中注册当前loop的selector,设置interestOps为0表示只注册不上报任何事件,并将自己(this/当前的channel)作为附件绑定到这个SelectableChannel中

    @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                 //这里就是JAVA NIO中的注册
                //这里的interestOps=0表示完成注册操作,不对任何事件感兴趣
                //第三个参数this比较重要,用于java的channel和netty的channel间的绑定关系
                selectionKey = javaChannel().register(eventLoop().selector, 0, this); 
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }
    

    类似doRegisiter,在AbstractNioChannel有多个do开头的函数。
    他们都是通过NioMessageUnsafe调用用于正真的底层实现。

    image.png

    实际上一直没有搞清楚为什么要在Channel里拆出一个Unsafe来,既然是防止用户调用,但是可以看到Channel里有unsafe()这个顶级接口。

    相关文章

      网友评论

        本文标题:Netty源码分析(四)Unsafe

        本文链接:https://www.haomeiwen.com/subject/dcxnxxtx.html