netty零拷贝之CompositeByteBuf

目录

        一、 背景简介

        二、 netty零拷贝


一、背景简介

在TCP网络数据传输过程中,数据包有可能被分割为独立的几个数据包进行发送,对于服务器接收端来说,单个的数据包是没有任何意义的,只有将这些数据包组合到一起,才能接收后解码然后提供给我们的业务层进行处理,而在数据报价进行整合的过程中,零拷贝做的工作就是避免或者减少内存区域中数据的复制次数,进而提高程序的响应性能。

二、 netty零拷贝

netty对于零拷贝有几种不同的形式,其本质上都是讲两个或者多个ByteBuf逻辑上组合到一起,物理上不一定是连续的,在操作数据时候, 不必再将一个ByteBuf拷贝到另一个ByteBuf中去. 因此少了内存的拷贝。而且netty的ByteBuf 支持 slice 操作, 可以将 ByteBuf 分解为多个 ByteBuf, 这些ByteBuf共享同一个内存区域,这样也就避免了内存的拷贝。

CompositeByteBuf 零拷贝:

CompositeByteBuf字面意思就是组合ByteBuf,他会把多个ByteBuf组合到一起,这些ByteBuf在逻辑上连续,实际物理地址不一定连续。

CompositeByteBuf中主要通过addComponent方法进行ByteBuf的合并。

进入到addComponent方法

    public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) {
        addComponents0(increaseWriterIndex, components.size(), buffers, 0, buffers.length);
        consolidateIfNeeded();
        return this;
    }

核心逻辑就是addComponent0方法,consolidateIfNeeded()仅仅是判断合并组件数是否超过当前操作允许的最大值,如果超过最大值,就会进行扩容操作,如下:

    private void consolidateIfNeeded() {
        // 合并组件数是否超过当前操作允许的最大值。
        final int numComponents = components.size();
        if (numComponents > maxNumComponents) {
            final int capacity = components.get(numComponents - 1).endOffset;
​
            ByteBuf consolidated = allocBuffer(capacity);
​
            // 我们没有使用foreach来避免创建迭代器。
            for (int i = 0; i < numComponents; i ++) {
                Component c = components.get(i);
                ByteBuf b = c.buf;
                consolidated.writeBytes(b);
                c.freeIfNecessary();
            }
            Component c = new Component(consolidated);
            c.endOffset = c.length;
            components.clear();
            components.add(c);
        }
    }

其扩容逻辑并不复杂。

继续来到addComponent0方法。

    private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] buffers, int offset, int len) {
        checkNotNull(buffers, "buffers");
        int i = offset;
        try {
            // 校验Components index
            checkComponentIndex(cIndex);
            // 无需合并
            while (i < len) {
                // Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
                // will release if an exception occurs, and we also release in the finally block here).
                ByteBuf b = buffers[i++];
                if (b == null) {
                    break;
                }
                cIndex = addComponent0(increaseWriterIndex, cIndex, b) + 1;
                int size = components.size();
                if (cIndex > size) {
                    cIndex = size;
                }
            }
            return cIndex;
        } finally {
            for (; i < len; ++i) {
                ByteBuf b = buffers[i];
                if (b != null) {
                    try {
                        b.release();
                    } catch (Throwable ignored) {
                        // 安静的释放ByteBuf
                    }
                }
            }
        }
    }

其核心逻辑就是通过while循环依次遍历每一个ByteBuf然后调用重载方法addComponent0进行添加每一个ByteBuf,最后将每一个ByteBuf进行内存释放。

接着进入重载方法addComponent0看看是如何添加每一个ByteBuf的。

    private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffer) {
        assert buffer != null;
        boolean wasAdded = false;
        try {
            checkComponentIndex(cIndex);
​
            int readableBytes = buffer.readableBytes();
​
            // 无需合并-只需将组件添加到列表中.
            @SuppressWarnings("deprecation")
            Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice());
            if (cIndex == components.size()) {
                wasAdded = components.add(c);
                if (cIndex == 0) {
                    c.endOffset = readableBytes;
                } else {
                    Component prev = components.get(cIndex - 1);
                    c.offset = prev.endOffset;
                    c.endOffset = c.offset + readableBytes;
                }
            } else {
                components.add(cIndex, c);
                wasAdded = true;
                if (readableBytes != 0) {
                    updateComponentOffsets(cIndex);
                }
            }
            if (increaseWriterIndex) {
                writerIndex(writerIndex() + buffer.readableBytes());
            }
            return cIndex;
        } finally {
            if (!wasAdded) {
                buffer.release();
            }
        }
    }

其核心逻辑不难分析,主要就是添加组件、改变偏移量和end偏移量,如果开启了写索引递增,那么就将写索引加上每个ByteBuf的可读字节,最后如果添加组件失败,就将当前组件ByteBuf进行release释放。

总的来说CompositeByteBuf是通过内部封装的List<Component> components将ByteBuf进行合并,在合并的过程中我们可以手动设置是否递增写索引,进而从逻辑上可以操作多个ByteBuf而不必在物理地址上将其拷贝。

个人才疏学浅、信手涂鸦,netty框架更多模块解读相关源码持续更新中,感兴趣的朋友请移步至个人公众号,谢谢支持😜😜......

公众号:wenyixicodedog

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值