对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

数据缓冲区与编解码器

Java NIO provides ByteBuffer but many libraries build their own byte buffer API on top, especially for network operations where reusing buffers and/or using direct buffers is beneficial for performance. For example Netty has the ByteBuf hierarchy, Undertow uses XNIO, Jetty uses pooled byte buffers with a callback to be released, and so on. The spring-core module provides a set of abstractions to work with various byte buffer APIs as follows:spring-doc.cadn.net.cn

DataBufferFactory

DataBufferFactory 用于以两种方式之一创建数据缓冲区:spring-doc.cadn.net.cn

  1. 分配一个新的数据缓冲区,可选地提前指定容量,如果已知的话,这样做更高效,即使DataBuffer的实现可以在需要时增长和缩小。spring-doc.cadn.net.cn

  2. Wrap an existing byte[] or java.nio.ByteBuffer, which decorates the given data with a DataBuffer implementation and that does not involve allocation.spring-doc.cadn.net.cn

请注意,WebFlux应用程序不会直接创建DataBufferFactory,而是通过客户端的ServerHttpResponseClientHttpRequest来访问它。 工厂的类型取决于底层客户端或服务器,例如NettyDataBufferFactory用于Reactor Netty,DefaultDataBufferFactory用于其他情况。spring-doc.cadn.net.cn

DataBuffer

The DataBuffer interface offers similar operations as java.nio.ByteBuffer but also brings a few additional benefits some of which are inspired by the Netty ByteBuf. Below is a partial list of benefits:spring-doc.cadn.net.cn

PooledDataBuffer

如Javadoc中所述, ByteBuffer, 字节缓冲区可以是直接的或非直接的。直接缓冲区可能位于Java堆外, 这消除了本地I/O操作时复制的需要。这使得直接缓冲区特别适用于通过套接字接收和发送数据,但它们创建和释放的成本也更高,因此引入了缓冲池的概念。spring-doc.cadn.net.cn

PooledDataBufferDataBuffer 的扩展,它有助于引用计数,这对于字节缓冲池非常重要。它是如何工作的?当分配一个 PooledDataBuffer 时,引用计数为 1。对 retain() 的调用会增加计数,而对 release() 的调用会减少计数。只要计数大于 0,缓冲区就保证不会被释放。当计数减少到 0 时,池中的缓冲区可以被释放,实际上这意味着缓冲区占用的内存可以返回到内存池。spring-doc.cadn.net.cn

请注意,通常情况下,最好使用 DataBufferUtils 中的方法,这些方法仅在 DataBufferPooledDataBuffer 的实例时才应用释放或保留操作,而不是直接操作 PooledDataBufferspring-doc.cadn.net.cn

DataBufferUtils

DataBufferUtils 提供了许多实用方法来操作数据缓冲区:spring-doc.cadn.net.cn

  • 将数据缓冲区流合并为一个缓冲区,可能采用零拷贝方式,例如通过复合缓冲区,如果底层字节缓冲区API支持的话。spring-doc.cadn.net.cn

  • InputStream 或 NIO Channel 转换为 Flux<DataBuffer>,反之将 Publisher<DataBuffer> 转换为 OutputStream 或 NIO Channelspring-doc.cadn.net.cn

  • Methods to release or retain a DataBuffer if the buffer is an instance of PooledDataBuffer.spring-doc.cadn.net.cn

  • 跳过或从字节流中读取直到达到特定的字节数。spring-doc.cadn.net.cn

编解码器

The org.springframework.core.codec 包提供了以下策略接口:spring-doc.cadn.net.cn

The spring-core 模块提供了 byte[]ByteBufferDataBufferResource、以及 String 编码器和解码器实现。The spring-web 模块增加了 Jackson JSON、Jackson Smile、JAXB2、Protocol Buffers 以及其他编码器和解码器实现。请参见 WebFlux 部分的Codecsspring-doc.cadn.net.cn

使用 DataBuffer

在处理数据缓冲区时,必须特别注意确保释放缓冲区,因为它们可能是可复用的。我们将使用编解码器来说明这一点,但这些概念更广泛地适用。让我们看看编解码器内部是如何管理数据缓冲区的。spring-doc.cadn.net.cn

一个 Decoder 是读取输入数据缓冲区的最后一个,然后创建更高级别的对象,因此它必须按如下方式释放它们:spring-doc.cadn.net.cn

  1. 如果一个Decoder简单地读取每个输入缓冲区并立即准备释放它,它可以通过DataBufferUtils.release(dataBuffer)来实现这一点。spring-doc.cadn.net.cn

  2. 如果一个 Decoder 使用了 FluxMono 操作符,例如 flatMapreduce 以及其他会内部预取和缓存数据项的操作符,或者使用了 filterskip 以及其他会排除项的操作符,那么必须将 doOnDiscard(DataBuffer.class, DataBufferUtils::release) 添加到组合链中,以确保在被丢弃之前释放这些缓冲区,可能还会由于错误或取消信号而触发。spring-doc.cadn.net.cn

  3. 如果 Decoder 以其他任何方式保留一个或多个数据缓冲区,它必须在完全读取后确保释放这些缓冲区,或者在缓存的数据缓冲区被读取和释放之前发生错误或取消信号时进行释放。spring-doc.cadn.net.cn

请注意,DataBufferUtils#join 提供了一种安全且高效的方式来聚合数据缓冲流到单一的数据缓冲区。同样,skipUntilByteCounttakeUntilByteCount 是解码器可以使用的其他安全方法。spring-doc.cadn.net.cn

An Encoder allocates data buffers that others must read (and release). So an Encoder doesn’t have much to do. However an Encoder must take care to release a data buffer if a serialization error occurs while populating the buffer with data. For example:spring-doc.cadn.net.cn

DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
try {
	// serialize and populate buffer..
	release = false;
}
finally {
	if (release) {
		DataBufferUtils.release(buffer);
	}
}
return buffer;
val buffer = factory.allocateBuffer()
var release = true
try {
	// serialize and populate buffer..
	release = false
} finally {
	if (release) {
		DataBufferUtils.release(buffer)
	}
}
return buffer

The consumer of an Encoder is responsible for releasing the data buffers it receives. In a WebFlux application, the output of the Encoder is used to write to the HTTP server response, or to the client HTTP request, in which case releasing the data buffers is the responsibility of the code writing to the server response, or to the client request.spring-doc.cadn.net.cn

请注意,在Netty上运行时,有用于排查缓冲区泄漏问题的调试选项。spring-doc.cadn.net.cn