Java NIO 提供,但许多库在上面构建了自己的字节缓冲区 API,
特别是对于重用缓冲区和/或使用直接缓冲区的网络操作
有利于性能。例如,Netty 具有层次结构,Undertow 使用
XNIO、Jetty 使用池字节缓冲区和要释放的回调,依此类推。
该模块提供了一组抽象来处理各种字节缓冲区
API如下:ByteBuffer
ByteBuf
spring-core
-
DataBufferFactory
将数据缓冲区的创建抽象化。 -
DataBuffer
表示一个字节缓冲区,可以将其池化。 -
DataBufferUtils
为数据缓冲区提供实用工具方法。 -
编解码器将数据缓冲区流解码或编码为更高级别的对象。
DataBufferFactory
DataBufferFactory
用于通过以下两种方式之一创建数据缓冲区:
-
分配新的数据缓冲区,可以选择预先指定容量(如果已知),即 即使实现可以按需增长和缩小,效率也会更高。
DataBuffer
-
包装现有的 或 ,它用 一个不涉及分配的实现。
byte[]
java.nio.ByteBuffer
DataBuffer
请注意,WebFlux 应用程序不会直接创建一个,而是创建一个
通过客户端的 OR 访问它。
工厂的类型取决于底层客户端或服务器,例如 对于Reactor Netty,对于其他人。DataBufferFactory
ServerHttpResponse
ClientHttpRequest
NettyDataBufferFactory
DefaultDataBufferFactory
DataBuffer
该界面提供与此类似的操作,但也
带来了一些额外的好处,其中一些是受 Netty 的启发。
以下是部分福利列表:DataBuffer
java.nio.ByteBuffer
ByteBuf
-
使用独立位置进行读写,即不需要调用 在读取和写入之间交替。
flip()
-
容量按需扩展。
java.lang.StringBuilder
-
通过
PooledDataBuffer
进行池化缓冲区和引用计数。 -
将缓冲区视为 、 或 。
java.nio.ByteBuffer
InputStream
OutputStream
-
确定给定字节的索引或最后一个索引。
PooledDataBuffer
正如 ByteBuffer 的 Javadoc 中所解释的, 字节缓冲区可以是直接缓冲区,也可以是非直接缓冲区。直接缓冲区可能驻留在 Java 堆之外 这消除了对本机 I/O 操作进行复制的需要。这使得直接缓冲区 对于通过套接字接收和发送数据特别有用,但它们也更有用 创建和发布成本高昂,这导致了池化缓冲区的想法。
PooledDataBuffer
是有助于参考计数的扩展
对于字节缓冲池是必不可少的。它是如何工作的?当 a 是
分配的引用计数为 1。调用以递增计数,而
呼吁减少它。只要计数大于 0,缓冲区就
保证不会被释放。当计数减少到 0 时,合并的缓冲区可以
released,这在实践中可能意味着缓冲区的保留内存将返回到
内存池。DataBuffer
PooledDataBuffer
retain()
release()
请注意,在大多数情况下,与其直接操作,不如直接操作
若要使用将 release 或 retain 应用于 的便捷方法,仅当它是 的实例时。PooledDataBuffer
DataBufferUtils
DataBuffer
PooledDataBuffer
DataBufferUtils
DataBufferUtils
提供了许多实用程序方法来对数据缓冲区进行操作:
-
将数据缓冲区流加入到单个缓冲区中,可能具有零复制,例如通过 复合缓冲区(如果基础字节缓冲区 API 支持)。
-
将 或 NIO 变成 ,反之亦然 a 变成 或 NIO 。
InputStream
Channel
Flux<DataBuffer>
Publisher<DataBuffer>
OutputStream
Channel
-
如果缓冲区是 的实例,则释放或保留 的方法。
DataBuffer
PooledDataBuffer
-
跳过或取字节流,直到特定的字节计数。
编 解码 器
该软件包提供以下策略接口:org.springframework.core.codec
-
Encoder
编码到数据缓冲区流中。Publisher<T>
-
Decoder
解码为更高级别的对象流。Publisher<DataBuffer>
该模块提供 、 、 、该模块添加了 Jackson JSON,
Jackson Smile、JAXB2、Protocol Buffers 和其他编码器和解码器。请参阅 WebFlux 部分中的编解码器。spring-core
byte[]
ByteBuffer
DataBuffer
Resource
String
spring-web
用DataBuffer
使用数据缓冲区时,必须特别小心以确保释放缓冲区 因为它们可以合并。我们将使用编解码器来说明 这是如何工作的,但这些概念更普遍地适用。让我们看看编解码器必须做什么 在内部管理数据缓冲区。
A 是创建更高级别之前最后一个读取输入数据缓冲区
对象,因此它必须按如下方式释放它们:Decoder
-
如果 a 只是读取每个输入缓冲区并准备好 立即释放它,它可以通过 .
Decoder
DataBufferUtils.release(dataBuffer)
-
如果 a 正在使用 或运算符,例如 、 和 其他在内部预取和缓存数据项,或者使用运算符(如 、 )和其他遗漏项的运算符,则必须添加到 组合链,以确保在丢弃之前释放此类缓冲液,可能 也是由于错误或取消信号。
Decoder
Flux
Mono
flatMap
reduce
filter
skip
doOnDiscard(DataBuffer.class, DataBufferUtils::release)
-
如果以任何其他方式保留一个或多个数据缓冲区,则必须 确保它们在完全读取时被释放,或者在出现错误或取消信号时被释放 在读取和释放缓存的数据缓冲区之前进行。
Decoder
请注意,这提供了一种安全有效的方法来聚合数据
缓冲流到单个数据缓冲区中。同样,并且是解码器使用的其他安全方法。DataBufferUtils#join
skipUntilByteCount
takeUntilByteCount
分配其他人必须读取(和释放)的数据缓冲区。所以安没什么可做的。但是,如果出现以下情况,必须注意释放数据缓冲区
使用数据填充缓冲区时发生序列化错误。例如:Encoder
Encoder
Encoder
-
Java
-
Kotlin
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
的使用者负责释放它接收的数据缓冲区。
在 WebFlux 应用程序中,输出用于写入 HTTP 服务器
响应,或对客户端 HTTP 请求的响应,在这种情况下,释放数据缓冲区是
将代码写入服务器响应或客户端请求的责任。Encoder
Encoder
请注意,在 Netty 上运行时,有一些调试选项可用于对缓冲区泄漏进行故障排除。