在netty中发送两个字节缓冲区(标头+正文)的最佳方法

Lan*_*Lan 5 netty

在netty中发送两个字节缓冲区(标头+主体)的最佳方法是什么

我在我们的项目中使用netty,现在我们将以以下格式发送数据

  1. header:int(32bit),包含body的长度
  2. 正文:字节[]

我正在寻找发送 header+body 最快的最佳方法,重点是我想避免数组复制,因为主体有大量数据。

1)创建一个新的byte[]并将body复制到其中

void sendData1(ChannelHandlerContext ctx, byte[] body) {
    byte[] newBuf = new byte[4+body.length];

    // header
    int len = body.length;
    newBuf[3] = (byte) (len & 0xff);
    len >>= 8;
    newBuf[2] = (byte) (len & 0xff);
    len >>= 8;
    newBuf[1] = (byte) (len & 0xff);
    len >>= 8;
    newBuf[0] = (byte) len;

    // body
    System.arraycopy(body, 0, newBuf, 4, body.length);

    final ByteBuf outBuf = Unpooled.wrappedBuffer(newBuf);
    ctx.writeAndFlush(outBuf);
}
Run Code Online (Sandbox Code Playgroud)

2)直接使用netty ByteBuf的write函数

void sendData2(ChannelHandlerContext ctx, byte[] body) {
    final ByteBuf outBuf = ctx.alloc().buffer(4+body.length);

    // header
    outBuf.writeInt(body.length);
    // body
    outBuf.writeBytes(body);

    ctx.writeAndFlush(outBuf);
}
Run Code Online (Sandbox Code Playgroud)

3)设置两个netty ByteBuf并分别发送出去然后flush()

void sendData3(ChannelHandlerContext ctx, byte[] body) {
    // header
    final ByteBuf headBuf = ctx.alloc().buffer(4);
    headBuf.writeInt(body.length);

    // body
    final ByteBuf bodyBuf = Unpooled.wrappedBuffer(body);

    ctx.write(headBuf);
    ctx.write(bodyBuf);
    ctx.flush();
}
Run Code Online (Sandbox Code Playgroud)

4)使用netty的CompositeByteBuf

void sendData4(ChannelHandlerContext ctx, byte[] body) {
    // header
    final ByteBuf headBuf = ctx.alloc().buffer(4);
    headBuf.writeInt(body.length);

    // body
    final ByteBuf bodyBuf = Unpooled.wrappedBuffer(body);

    CompositeByteBuf composite = ctx.alloc().compositeBuffer();
    composite.addComponents(headBuf, bodyBuf);
    ctx.writeAndFlush(composite);
}
Run Code Online (Sandbox Code Playgroud)

选项1)和选项2)将进行数组复制,我认为它们会有相同的性能?选项3)我不确定它是否会进行数组复制,但它会调用 ctx.write() 两次,我认为这很昂贵;选项4)我不确定它是否有效。但我在netty5中尝试过,似乎它只发送头部。

您正在使用哪一款或者您有什么好的选择吗?

多谢!

Sco*_*ell 4

首先是标准免责声明...

性能通常归结为您的用例、软件和硬件配置。对于 Netty 来说,软件配置的一个重要组成部分是管道有多大以及管道中的内容将作用于您的写入。性能还至少有 2 个主要组成部分:运行时间、内存(并且可以经常进行权衡以优化其中之一)。为了获得适合您的用例的最佳解决方案,我建议运行不同的场景,看看什么可以为您提供最佳性能。

接下来是一些实际观察:

  1. 似乎是2的更复杂的版本。
  2. 正如您所指出的,您正在将数据复制到 Netty 的缓冲区中。
  3. ctx.write()不一定要复制。这取决于您的管道以及正在使用的分配器。无论您采用哪种方法,都必须将数据写入通道。唯一的区别是您是在应用层复制数据还是让通道直接将其写出。
  4. CompositeByteBuf - 没有使用这么多,但处理缓冲区集合会产生一些开销。如果您可以手动分配缓冲区并直接调用 write 那么我将无法提供使用它的理由。通过自己管理缓冲区,您可能会为 CompositeByteBuf 的每一个方法进行循环展开。

您研究过PooledByteBufAllocatordirectBuffer吗?如果您的用例允许,这些可能会带来一些性能优势。池化分配器意味着您只需为 Java 的缓冲区自动归零付费一次,并且还可能会减少 GC 活动。有关直接缓冲区ByteBuffer.allocate() 与 ByteBuffer.allocateDirect()的一般描述,请参阅此问题。