程序猿之路

Netty源码分析(四)Unsafe

2017-05-27  本文已影响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()这个顶级接口。

上一篇下一篇

猜你喜欢

热点阅读