【netty in action】学习笔记-第一章 了解java NIO(2)

上一篇文章了解了java nio的一些特性和基本用法。本篇继续来看看java nio有哪些问题以及netty如何解决这些问题。

跨平台和兼容性问题

java nio有nio和nio2两个版本,后者只支持jdk7。而且java nio本身属于比较low level的api,有时候会遇到在linux运行良好但是在windows上却有问题。

netty提供统一的api,你不需要关注java的版本,也不需要关注操作系统。

ByteBuffer的扩展

通过前面的示例,你能看出来java nio的ByteBuffer并不好用,比如还有自己切换读写模式。netty扩展了ByteBuffer提供更加易用的API。具体的用法在后面章节的笔记中会详细说明。

内存泄漏的问题

nio有个Scattering and Gathering的概念,就是分散读取,集中写入。

image

image
scatter(分散)是指数据从一个channel读取到多个buffer中。比如下面的例子:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body }
channel.read(bufferArray);

read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。

集中读的概念就是反过来,多个buffer的数据写入到同一个channel。示例如下:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);

java nio提供了专门的接口来处理Scattering and Gathering

public interface ScatteringByteChannel extends ReadableByteChannel
{
    public long read(ByteBuffer[] dsts) throws IOException;

    public long read(ByteBuffer[] dsts, int offset, int length) throws IOException;
}

public interface GatheringByteChannel extends WritableByteChannel
{
    public long write(ByteBuffer[] srcs) throws IOException;

    public long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
}

比如SocketChannel就实现了这两个接口。

image

但是Scattering and Gathering功能会导致内存泄露,一直到Java7才解决内存泄露问题。

epoll的缺陷问题

这是个著名的bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该BUG发生概率降低了一些而已,它并没有被根本解决。

while (true) {
            int selected = selector.select();
            Set<SelectedKeys> readyKeys = selector.selectedKeys();
            Iterator iterator = readyKeys.iterator();
            while (iterator.hasNext()) {
            //do something
            }
        }

解决epoll bug的唯一方法是回收旧的选择器,将先前注册的通道实例转移到新创建的选择器上。而netty正是基于此方法提供的解决方案。

netty 对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug。然后netty会重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。

这个问题的详细情况和解决方案,就不做这里展开了,有兴趣的可以网上搜索下。


犀牛饲养员
277 声望271 粉丝