netty源码分析channel初始msg

这篇博客深入探讨了 Netty 中 ByteBuf 的内存分配和管理。通过分析服务端和客户端代码,展示了 ByteBuf 在 NioServerSocketChannel 和 NioSocketChannel 中的使用。博客详细追踪了 ByteBuf 的创建过程,从池化与否、直接内存与堆内存的选择,到初始容量的确定。最后,揭示了如何通过系统属性和配置来控制 ByteBuf 的分配逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面是服务端基础结构,在channelRead处打断点,分析channel中msg的来源

@Slf4j
public class TestByteBuf {
    public static void main(String[] args) {
        ByteBufAllocator.DEFAULT.buffer();
        new ServerBootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) {
                        ch.pipeline().addLast(new LoggingHandler());
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
     /**打断点**/            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//                                ByteBuf buf = ctx.alloc().buffer();
//                                log.debug("alloc buf {}", buf);

                                log.debug("receive buf {}", msg);
                                System.out.println("");
                            }
                        });
                    }
                }).bind(8080);
    }
}

需要一个客户端来触发

@Slf4j
public class TestBacklogClient {
    public static void main(String[] args) {
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(worker);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new LoggingHandler());
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                        @Override
                        public void channelActive(ChannelHandlerContext ctx) throws Exception {
                            ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("hello!".getBytes()));
                        }
                    });
                }
            });
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("client error", e);
        } finally {
            worker.shutdownGracefully();
        }
    }
}

我们msg的类型为:PooledUnsafeDirectByteBuf池化并使用直接内存的字节缓冲数组
在这里插入图片描述

沿着调用链往前找,跳过Handler,跳过PipeLine,找到初始位置msg生成的部分
这里定位到AbstractNioByteChannel类
在这里插入图片描述
找到和msg相同类型的byteBuf,就是这块
在这里插入图片描述
网上跳,发现byteBuf的分配方式如下

byteBuf = allocHandle.allocate(allocator);

我们分三个方面跟踪

1.确定池化/非池化
上文的allocator是使用

final ByteBufAllocator allocator = config.getAllocator();

我们一直向上跟

ChannelConfig.getAllocator()->DefaultChannelConfig.getAllocator()->ByteBufAllocator.DEFAULT->ByteBufUtil.DEFAULT_ALLOCATOR

然后是下面:可以清晰的看到使用逻辑是unpooled

    static final ByteBufAllocator DEFAULT_ALLOCATOR;

    static {
        String allocType = SystemPropertyUtil.get(
                "io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
        allocType = allocType.toLowerCase(Locale.US).trim();

        ByteBufAllocator alloc;
        if ("unpooled".equals(allocType)) {
            alloc = UnpooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: {}", allocType);
        } else if ("pooled".equals(allocType)) {
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: {}", allocType);
        } else {
            alloc = PooledByteBufAllocator.DEFAULT;
            logger.debug("-Dio.netty.allocator.type: pooled (unknown: {})", allocType);
        }

        DEFAULT_ALLOCATOR = alloc;

2.确定使用直接内存/堆内存
Step Into跟踪allocate方法,找到如下分配内存方式,使用ioBuffer为直接内存

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

3.确定初始容量
跟踪recvBufAllocHandle()方法

AbstractChannel.recvBufAllocHandle()->ChannelConfig.getRecvByteBufAllocator()->DefaultChannelConfig.getRecvByteBufAllocator()->setRecvByteBufAllocator()->DefaultChannelConfig()->AdaptiveRecvByteBufAllocator.AdaptiveRecvByteBufAllocator()

然后找到如下

//控制大小
    public AdaptiveRecvByteBufAllocator() {
        this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM);
    }
    static final int DEFAULT_MINIMUM = 64;
    static final int DEFAULT_INITIAL = 1024;
    static final int DEFAULT_MAXIMUM = 65536;

确定了msg的初始容量为1024

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值