前面的文章分析了eventloopgroup以及eventloop的继承体系,在这篇文章主要分析以下netty对channel的封装,我只对nio感兴趣,那么就只分析对nio这一部分的封装。。。。
主要来分析以下的继承体系:
channel是netty定义的顶层接口,其无非就是定义了一些基本的方法,例如返回当前channel所属的eventloop,判断channel是否已经打开,是否已经注册,本地地址,连接的远程地址等。
另外它还定义了一个十分重要的内部接口Unsfe,在这个里面的定义了一些具体的数据传输的方法,官方给出的注释给出的说明是:这里面定义的方法不应该被用户空间的代码所直接调用。。。
另外,channel还继承了AttributeMap,ChannelOutboundInvoker,ChannelPropertyAccess,Comparable接口。。。
接下来就是AbstractChannel,一个夹在中间的抽象类,它实现了一些基本的方法,另外还定义了一些属性,首先我们来看它定义的一些属性:
static final ConcurrentMap<Integer, Channel> allChannels = PlatformDependent.newConcurrentHashMap(); //静态属性,用于保存所有的channel,每个channel都对应一个负数的ID
allChannels是一个静态属性,是一个ConcurrentHashMap,主要是用于保存和管理当前存在的所有channel,它的索引是一个负数的ID
private final Channel parent; //父channel
private final Integer id; //当前channel的id
private final Unsafe unsafe; //usafe对象
private final DefaultChannelPipeline pipeline; //当前channel的pipeline
private final ChannelFuture succeededFuture = new SucceededChannelFuture(this, null);
private final VoidChannelPromise voidPromise = new VoidChannelPromise(this);
private final CloseFuture closeFuture = new CloseFuture(this); //close时候用到的future
protected final ChannelFlushPromiseNotifier flushFutureNotifier = new ChannelFlushPromiseNotifier();
private volatile SocketAddress localAddress; //本地地址
private volatile SocketAddress remoteAddress; //连接的远程地址
private volatile EventLoop eventLoop; //所属的eventloop
private volatile boolean registered; //是否已经register
上述都是一些基本的属性定义,比较重要的有定义了一个Unsafe,以及pipeline,,我们可以来看一些AbstractChannel定义的构造方法:
//abstractchannel的构造函数
protected AbstractChannel(Channel parent, Integer id) {
if (id == null) {
id = allocateId(this); //为当前的channel分配一个负数的id
} else {
if (id.intValue() < 0) {
throw new IllegalArgumentException("id: " + id + " (expected: >= 0)");
}
if (allChannels.putIfAbsent(id, this) != null) {
throw new IllegalArgumentException("duplicate ID: " + id);
}
}
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = new DefaultChannelPipeline(this); //构造pipeline
//为closefuture添加一个listeer,也就是close的时候将当前channel从allChannels中移除
closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
allChannels.remove(id());
}
});
}
还是比较简单的,首先为当前的channel分配一个id,然后构造unsfae对象,以及pipeline对象,这里newUnsafe方法是一个抽象方法,也就意味着真正unsafe的定义还要延后到子类当中去,最后还未closeFuture添加了一个listener,当channel被关闭的时候将会被调用,操作也非常简单,无非就是从allChannels静态属性中将当前channel移除。。这样垃圾回收也就能回收了。。。
另外还要分析以下在AbstractChannel中定义的AbstractUnsafe,在它里面实现了一些基本的顶层方法,我个人觉得最为重要的是register方法,该方法的作用就是将当前channel注册到eventLoop当中,不过其实最终又会调用doRegister方法,这个方法将会在AbstractNioChannel中被重写。。。
最后来分析一下AbstractNioChannel,其实它也是一个抽象类,不过它定义的一些属性和方法就很有Nio的意思了。。首先还是来分析一下它的一些属性。。。
private final SelectableChannel ch; //真正用到的channel
protected final int readInterestOp; //真正关心的读事件
private volatile SelectionKey selectionKey; //注册后获取的的key
稍微熟悉一点Nio的人应该就能知道这几个东西是干嘛的吧。。。首先定义了一个SelectableChannel,它是一个java Nio定义的channel,另外readInterestOp用于区分当前channel所关注的的事件类型,至于selectionKey这东西就更为直白了,它是将 SelectableChannel注册到selector后得到的。。。。
从上面一些属性的定义就可以看出,在AbstractNioChannel中就已经将Netty的channel和java nio的channel联系起来了。。
还是来看一下它的构造函数吧:
//构造函数
protected AbstractNioChannel(
Channel parent, Integer id, SelectableChannel ch, int readInterestOp) {
super(parent, id);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false); //将channel设置为非阻塞
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
这里面比较有意思也是比价直白的就是将当前的selectableChannel设置为非阻塞的,看到这里就有点觉得,其实所有的编程语言和平台他们最终的实现都差不多,不管是c语言还是java,他们最终编程套路都差不多,起码在netty中我觉得和c语言的io框架的实现其实也都是差不太多的,唯一可能比较不一样的地方就是java的类封装比较漂亮,不过也稍微显的比较繁琐,没有c语言来的直白。。。
另外最后还要分析一下在AbstractNioChannel中定义的AbstractNioUnsafe,不过这里我只是想分析一下其中定义的一个方法,也就是在上面提到过的doRegister方法:
@Override
//channel的regist
protected Runnable doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
//获取实际的channel然后注册到eventloop的selector上面
selectionKey = javaChannel().register(eventLoop().selector, 0, this);
return null;
} 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;
}
}
}
}
上面的代码就将netty的channel和java nio完全的联系起来了。。。这里eventLoop方法返回的是一个NioEventLoop对象,在它里面定义了selector属性,doRegister的主要作用就是将当前对象的selectableChannel注册到eventLoop的selector去,并保存返回的key。
好了,这篇文章好像写的比较多了,不过它确实比较重要,清清楚楚的将netty与java nio联系了起来。。。。