解读Netty之接收缓冲区

概述

用过netty,大家都知道在请求处理之前,会有一个缓冲区用于接受数据,不同场景对缓冲的大小都不太一样。
比如UDP协议的DatagramChannel,默认缓冲区大小只给了2048,而假如开发一个SyslogUdp的协议服务,大小其实就不止这么点。

因此,缓冲区怎么用,怎么设置就非常关键啦,很不小心就会踩坑,本文主要给大家讲解下netty4下接受缓冲区的原理及对源码进行解读…

结构分解

Netty的接收缓冲区,是由接口类RecvByteBufAllocator实现而来,该类有个子接口Handle,包含了三个方法:

    interface Handle {
        /**
         * 创建一个合适大小的接收缓冲区(大到足够读取所有inbound数据,小到不会存在数据浪费)
         */
        ByteBuf allocate(ByteBufAllocator alloc);

        /**
         * 猜测这个缓冲区的容量,之所有是猜测,是因为存在动态扩容的一种缓冲区
         */
        int guess();

        /**
         * 记录上一次读操作实际读取的字节数,以便接收缓冲区能动态调整一个合适的容量
         *
         * @param actualReadBytes the actual number of read bytes in the previous read operation
         */
        void record(int actualReadBytes);
    }

了解完基类,应该可以知道设计者的意图,提供多种灵活方式来创建接收缓冲区,比如固定空间大小的,空间动态扩容缩的等…

这个有什么好处呢,对比起JDK原生的NIO类库使用的java.nio.ByteBuffer,实际是一个固定长度的byte数组,这也说明原生buffer无法动态扩容,相关代码如下:

public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>
{
   

    // These fields are declared here rather than in Heap-X-Buffer in order to
    // reduce the number of virtual method invocations needed to access these
    // values, which is especially costly when coding small buffers.
    //
    final byte[] hb;                  // Non-null only for heap buffers
    final int offset;
    boolean isReadOnly;                 // Valid only for heap buffers

讲好处前先踩踩原生ByteBuffer固定空间的坏处,例如开发人员一开始很难预测到每条消息报文的长度,或者消息堆积空间所需大小,当然你可以说干脆直接分配一个比较大的ByteBuffer,这通常没问题,不过对于海量推送、高并发等场景,这会给服务器带来沉重的内存负担,也算是一种资源浪费啊。
举个详细点例子:例如海量推送服务,单条消息若最大上限是16K,消息平均大小是6K,为了满足消息支持16K的处理,我们需要把buffer设置成16K,这样子的话,因是海量链路推送,那么假如并发链接数为100w,每个链路都有独立的ByteBuffer接收缓冲区,那将会额外损耗的总内存为:100,0000 * (16K-6K) = 9764M。是不是吓一跳,竟然快要消耗一个G的内存了,大内存不仅增加硬件成本,而且会导致长时间的FGC,对系统的维护和稳定保障带来非常大的冲击。

那讲完坏处,相比好处就显而易见啦,能灵活调整内存空间大小,是一件多么棒的事情。实际上RecvByteBufAllocator提供了两种实现,分别是:AdaptiveRecvByteBufAllocator和FixedRecvByteBufAllocator

我们先来看看简单的FixedRecvByteBufAllocator,代码逻辑如下:

public class FixedRecvByteBufAllocator implements RecvByteBufAllocator {
   

    private static final class HandleImpl implements Handle {
   

        private final int bufferSize;

        HandleImpl(int bufferSize) {
            this.bufferSize = bufferSize;
        }

        @Override
        public ByteBuf allocate(ByteBufAllocator alloc) {
            return alloc.ioBuffer(bufferSize);
        }

        @Override
        public int guess() {
            return bufferSize;
        }

        @Override
        public void record(int actualReadBytes) { }
    }

    private final Handle handle;

    /**
     * Creates a new predictor that always returns the same prediction of
     * the specified buffer size.
     */
    public FixedRecvByteBufAllocator(int bufferSize) {
        if (bufferSize <= 0) {
            
  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值