netty新版本源码阅读(4.0.9.final)

来源:互联网 发布:农村淘宝网点查询系统 编辑:程序博客网 时间:2024/05/08 01:50

好久没有看netty了,今天无意中一个东西用到了netty,发现原来新版本的final都已经出来了。。

但是我以前看的都是4.0的rc版本。。。瞬间觉得好郁闷。。。

不过起码以前看过了netty的代码,它的主要的脉络还是一样的。。

在这里还要赞一下final版本,它对数据的处理变得更简单了,,看起来更清晰。。。。

这里就拿数据的读取来举例子吧:

(1)还是先从nioeventloop中获取有数据可以读的channel

(2)利用channel的unsafe对象来将数据读取出来

(3)调用当前channel的pipeline上的fireChannelRead方法,用于从pipeline上由前向后的调用handler,并用合适的handler来处理刚刚读取的数据,这里所谓的合适就是inboundhandler,最新版本不再分byte类型和message类型了,这个其实思路比老版本更清晰。。。


好了,那么接下来还是进入老话题,开始看进入源代码,不过要记住这里看的已经是最新版本的源代码了,那么就从数据读取的入口开始,也就是processSelectedKey函数:

    private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {        final NioUnsafe unsafe = ch.unsafe();        if (!k.isValid()) {            // close the channel if the key is not valid anymore            unsafe.close(unsafe.voidPromise());            return;        }        try {            int readyOps = k.readyOps();            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {                unsafe.read();  //调用当前channel的unsafe对象来读取数据                if (!ch.isOpen()) {                    // Connection already closed - no need to handle write.                    return;                }            }            if ((readyOps & SelectionKey.OP_WRITE) != 0) {                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write                ch.unsafe().forceFlush();            }            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking                // See https://github.com/netty/netty/issues/924                int ops = k.interestOps();                ops &= ~SelectionKey.OP_CONNECT;                k.interestOps(ops);                unsafe.finishConnect();            }        } catch (CancelledKeyException e) {            unsafe.close(unsafe.voidPromise());        }    }
其实这部分代码没有太大的变化,直接就是获取当前的channel,然后调用该channel的unsafe对象的read函数来进行下一步的处理就是了。。。。

好吧,那么接下来我们来看看unsafe对象的read方法做了什么工作吧:

    private final class NioByteUnsafe extends AbstractNioUnsafe {        private RecvByteBufAllocator.Handle allocHandle;        @Override        public void read() {            assert eventLoop().inEventLoop();            final SelectionKey key = selectionKey();            final ChannelConfig config = config();            if (!config.isAutoRead()) {                int interestOps = key.interestOps();                if ((interestOps & readInterestOp) != 0) {                    // only remove readInterestOp if needed                    key.interestOps(interestOps & ~readInterestOp);                }            }                        //当前channel的pipeline            final ChannelPipeline pipeline = pipeline();            RecvByteBufAllocator.Handle allocHandle = this.allocHandle;            if (allocHandle == null) {                this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle();            }            final ByteBufAllocator allocator = config.getAllocator();  //buffer的allocater            final int maxMessagesPerRead = config.getMaxMessagesPerRead();            boolean closed = false;            Throwable exception = null;            ByteBuf byteBuf = null;            int messages = 0;            try {                for (;;) {                    byteBuf = allocHandle.allocate(allocator);                    int localReadAmount = doReadBytes(byteBuf); //将数据读进来                    if (localReadAmount == 0) {                        byteBuf.release();                        byteBuf = null;                        break;                    }                    if (localReadAmount < 0) {                        closed = true;                        byteBuf.release();                        byteBuf = null;                        break;                    }                    pipeline.fireChannelRead(byteBuf);  //调用pipeline上面的handler来处理数据                    allocHandle.record(localReadAmount);  //记录已经读取的数据量                    byteBuf = null;                    if (++ messages == maxMessagesPerRead) {                        break;                    }                }            } catch (Throwable t) {                exception = t;            } finally {                if (byteBuf != null) {                    if (byteBuf.isReadable()) {                        pipeline.fireChannelRead(byteBuf);                    } else {                        byteBuf.release();                    }                }                pipeline.fireChannelReadComplete(); //表示读取已经完成了。。。                if (exception != null) {                    if (exception instanceof IOException) {                        closed = true;                    }                    pipeline().fireExceptionCaught(exception);                }                if (closed) {  //有异常了,那就需要关掉                    setInputShutdown();                    if (isOpen()) {                        if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) {                            key.interestOps(key.interestOps() & ~readInterestOp);                            pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);                        } else {                            close(voidPromise());                        }                    }                }            }        }    }
这部分就出现了与老版本不太一样的地方吧,读取了数据之后,在unsafe对象里面调用pipeline的fireChannelRead来处理读取进来的数据,记得在老版本里面实在外面调用这个方法的吧,。。。

不管了,都是小事情,那么下面来看看pipeline上上面有什么更新么。。

    //表示当前channel有数据读了进来,保存在msg里面,一般情况下它就是一个bytebuf    //从头开始向后找inboundhandler来处理数据    public ChannelPipeline fireChannelRead(Object msg) {        head.fireChannelRead(msg);        return this;    }
到这里并没有什么好多说的,直接来看fireChannelRead方法吧:
    public ChannelHandlerContext fireChannelRead(final Object msg) {        if (msg == null) {            throw new NullPointerException("msg");        }        //找到第一个inboundhandler就可以了,这里不再分byte和message类型什的        final DefaultChannelHandlerContext next = findContextInbound();        EventExecutor executor = next.executor();        if (executor.inEventLoop()) {            next.invokeChannelRead(msg);  //调用方法来处理数据        } else {            executor.execute(new Runnable() {                @Override                public void run() {                    next.invokeChannelRead(msg);                }            });        }        return this;    }
这里findcontextinbound的时候,就不会再区分什么byte类型或者message类型的handler了,统一就一种类型,那就是object,其实这样一来反而更简单了,没有以前那么繁琐,开发人员难道不知道自己要处理的数据类型是什么么。。以前真的有点画蛇添足的感觉。。。而且也没有了什么buff用于存储这些数据,从开始就只有这一个数据,你爱怎么处理怎么处理
    private void invokeChannelRead(Object msg) {        try {            ((ChannelInboundHandler) handler).channelRead(this, msg);        } catch (Throwable t) {            notifyHandlerException(t);        }    }
另外这里也有跟老版本方法有不同的地方,这里是channelRead方法,直接来处理这个数据,而以前是inboundbufferupdated方法,听名字就觉得现在的方法更清晰。。。

也就是说我们现在的handler里面也将会使用channelRead方法了。。。。


到这里整个数据的读取流程就差不多了。。。

总的来说,主要的核心部分与老版本还是保持一致的,不过新版本的netty变的更简洁了。。赞一下。。

原创粉丝点击