Java NIO提供ByteBuffer,但许多库在顶部构建自己的字节缓冲区API,尤其是对于重用缓冲区和/或使用直接缓冲区有利于性能的网络操作。 例如,Netty具有ByteBuf层次结构,Undertow使用XNIO,Jetty使用pooled字节缓冲区并释放回调,依此类推。 spring-core模块提供了一组抽象来处理各种字节缓冲区API,如下所示:
DataBufferFactory用以下两种方式之一创建数据缓冲区:
请注意,WebFlux应用程序不直接创建DataBufferFactory,而是通过ServerHttpResponse或客户端的ClientHttpRequest访问它。 工厂类型取决于底层客户端或服务器,例如 Reactor Netty的NettyDataBufferFactory,其他的DefaultDataBufferFactory。
DataBuffer接口提供与java.nio.ByteBuffer类似的操作,但也带来了一些额外的好处,其中一些受Netty ByteBuf的启发。 以下是部分列表:
正如Javadoc for ByteBuffer中所解释的,字节缓冲区可以是直接缓冲区,也可以是非直接缓冲区。直接缓冲区可以驻留在Java堆之外,这样就无需复制本机I / O操作。这使得直接缓冲区对于通过套接字接收和发送数据特别有用,但是创建和释放它们也更加昂贵,这导致了pooling缓冲区的想法。
PooledDataBuffer是DataBuffer的扩展,它有助于引用计数,这对于字节缓冲池是必不可少的。它是如何工作的?当分配PooledDataBuffer时,引用计数为1.调用retain()递增计数,而对release()的调用则递减计数。只要计数大于0,就保证缓冲区不被释放。当计数减少到0时,可以释放pooled缓冲区,这实际上可能意味着缓冲区的保留内存返回到内存池。
请注意,不是直接对PooledDataBuffer进行操作,在大多数情况下,最好使用DataBufferUtils中的方法,只有当它是PooledDataBuffer的实例时才应用release或retain到DataBuffer。
DataBufferUtils提供了许多用于操作数据缓冲区的实用方法:
org.springframework.core.codec 包提供了下面的策略接口:
spring-core模块提供byte[],ByteBuffer,DataBuffer,Resource和String编码器和解码器实现。 spring-web模块增加了Jackson JSON,Jackson Smile,JAXB2,Protocol Buffers和其他编码器和解码器。 请参阅WebFlux部分中的编解码器。
使用数据缓冲区时,必须特别注意确保缓冲区被释放,因为它们可能被pooled。我们将使用codecs来说明它是如何工作的,但概念更普遍适用。让我们看看codecs在内部管理数据缓冲区时做了什么。
Decoder是在创建更高级别对象之前,最后一个读取输入数据缓冲区的,因此必须按如下方式释放它们:
请注意,DataBufferUtils#join提供了一种安全有效的方法,可将数据缓冲区流聚合到单个数据缓冲区中。同样,skipUntilByteCount和takeUntilByteCount是decoders使用的其他安全方法。
Encoder分配其他人必须读取(和释放)的数据缓冲区。所以Encoder没什么可做的。但是,如果在使用数据填充缓冲区时发生序列化错误,则Encoder必须注意释放数据缓冲区。例如:
DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
try {
// serialize and populate buffer..
release = false;
}
finally {
if (release) {
DataBufferUtils.release(buffer);
}
}
return buffer;
Encoder的使用者负责释放它接收的数据缓冲区。 在WebFlux应用程序中,Encoder的输出用于写入HTTP服务器响应或客户端HTTP请求,在这种情况下,释放数据缓冲区是代码写入服务器响应或客户端的责任。
请注意,在Netty上运行时,可以使用调试选项来排除缓冲区泄漏。