设置Netty接收Buff为堆内存模式

转自:https://sanwen8.cn/p/1bfoy1F.html

Netty为了提升报文的读写性能,默认会采用“零拷贝”模式,即消息读取时使用非堆的DirectBuffer来减少ByteBuffer的内存拷贝,如下图所示:



如果需要修改接收Buffer的类型,例如从DirectByteBuf修改为HeapByteBuf,首先需要在初始化Channel的时候对接收缓冲区进行设置,客户端代码示例如下:


 b.group(group)

.channel(NioSocketChannel.class)

 .option(ChannelOption.TCP_NODELAY, true)

 .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)

 .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)


如果是服务端,则需要在链路创建之后,初始化时对Channel的SocketChannelConfig的Allocator属性进行设置,代码如下:


.childHandler(new ChannelInitializer<SocketChannel>() {           

 @Override

  public void initChannel(SocketChannel ch) throws Exception {

  ch.config().setAllocator(UnpooledByteBufAllocator.DEFAULT);


无论是通过ChannelOption.RCVBUF_ALLOCATOR还是Allocator都只能设置Allocator的类型,无法直接设置ByteBufAllocator分配的ByteBuf类型。下面我们接着分析消息读取时的ByteBuf分配机制。消息读取时,调用的是NioByteUnsaferead方法,代码如下:





首先从SocketChannelConfig中获取ByteBufAllocator,在这里就是UnpooledByteBufAllocator,然后调用它的ioBuffer方法分配内存,它的具体实现如下:


 @Override

    public ByteBuf ioBuffer() {

        if (PlatformDependent.hasUnsafe()) {

            return directBuffer(0);

        }

        return heapBuffer(0);

    }


如果非Unsafe模式,则会使用堆内存heapBuffer,接着看hasUnsafe方法实现,它最终会调用如下方法:




设置io.netty.noUnsafe属性为true,则默认会使用Heap堆内存创建ByteBuf,下面我们在启动时设置-Dio.netty.noUnsafe为true进行测试




设置完成之后,使用Echo程序进行测试,测试结果如下:


总结

利用ch.config().setAllocator或Bootstrap.option(ChannelOption.ALLOCATOR, ByteBufAllocator),结合-Dio.netty.noUnsafe,可以灵活的在如下四种ByteBuf之间进行切换:

  • UnpooledHeapByteBuf

  • PooledHeapByteBuf

  • UnpooledDirectByteBuf

  • PooledDirectByteBuf

在Java中,使用Netty处理网络通信时,如果接收到的数据不完整,通常是由于数据分片或超时导致的。Netty默认会把接收到的数据作为一个完整的`ByteBuf`处理,如果你需要接收分块数据,可以采取以下步骤: 1. **启用分片模式**(Chunked mode): 将你的`ChannelInboundHandlerAdapter`配置成支持分片的,例如,你可以创建一个`MessageSizeBasedFrameDecoder`,它会在每个长度字段后的帧结束处拆分数据。 ```java ByteBufAllocator alloc = ... // 获取ByteBufAllocator实例 ByteBuf/frameDecoder = alloc.directBuffer(4); // 创建一个用于存储长度信息的buffer frameDecoder.writeInt(messageLength); channel.pipeline().addFirst("frameDecoder", new MessageSizeBasedFrameDecoder(frameDecoder)); ``` 2. **检查接收到的数据**: 在`ChannelHandlerContext#channelRead(ChannelHandlerContext, Object)`回调中,你需要检查接收到的是不是完整的帧。如果是部分帧,保存下来,等待后续数据到来。 3. **合并分片**: 当所有分片都接收完成后,将它们合并到一起。你可以使用`ByteBuf#readBytes(ByteBuf)`方法或自定义逻辑来完成这个过程。 4. **异常处理**: 要考虑可能出现的超时、连接中断等情况,并在适当的地方处理异常,避免阻塞线程。 ```java channel.pipeline().addLast(new ExceptionHandler() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值