Netty源码分析(四)Unsafe
2017-05-27 本文已影响264人
三斤牛肉
这里的Unsafe并不是我们常说的Java自带的sun.misc.Unsafe,而是io.netty.channel.Channel#Unsafe。为什么叫Unsafe呢,按Doc上说法是“Unsafe函数不允许被用户代码使用,这些函数是真正用于数据传输操作,必须被IO线程调用”。 也就是说真正依赖于底层协议/方案的实现是通过Unsafe包装出去的。
我们先看下Unsafe定义了哪些接口:
是不是很熟悉
regisiter bind connect write disconnect close flush beginRead
这些都是IO操作中经常用到的接口。
我们再来看一个具体实现,上一节中我们用了NioServerSocketChannel,这节我们就用其父类AbstractNioMessageChannel中定义的NioMessageUnsafe
先看下该类的继承情况:
先看下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调用用于正真的底层实现。
实际上一直没有搞清楚为什么要在Channel里拆出一个Unsafe来,既然是防止用户调用,但是可以看到Channel里有unsafe()这个顶级接口。