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

好久没有看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变的更简洁了。。赞一下。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值