Spring Data Buffers and Codecs

文章目录

Java NIO提供了ByteBuffer,但是许多库在顶部构建了自己的字节缓冲区API,特别是对于网络操作,其中重用缓冲区和/或使用直接缓冲区对性能有利。例如,Netty具有ByteBuf层次结构,Undertow使用XNIO,Jetty使用池字节缓冲区以及要释放的回调,依此类推。 spring-core模块提供了一组抽象,可与各种字节缓冲区API配合使用,如下所示:

  • DataBufferFactory抽象数据缓冲区的创建。
  • DataBuffer表示一个字节缓冲区,可以将其合并。
  • DataBufferUtils提供了用于数据缓冲区的实用程序方法。
  • 编解码器将流数据缓冲区流解码或编码为更高级别的对象。

8.1 DataBufferFactory

DataBufferFactory用于以以下两种方式之一创建数据缓冲区:

  1. 分配一个新的数据缓冲区,可以选择预先指定容量(如果已知),即使DataBuffer的实现可以按需增长和缩小,该容量也会更有效。
  2. 包装一个现有的byte []或java.nio.ByteBuffer,它用DataBuffer实现装饰给定的数据,并且不涉及分配。

请注意,WebFlux应用程序不会直接创建DataBufferFactory,而是通过客户端的ServerHttpResponse或ClientHttpRequest访问它。工厂的类型取决于基础客户端或服务器,例如NettyDataBufferFactory用于Reactor Netty,DefaultDataBufferFactory用于其他。

8.2 DataBuffer

DataBuffer接口提供与java.nio.ByteBuffer类似的操作,但还带来了一些其他好处,其中一些是受Netty ByteBuf启发的。以下是部分好处清单:

  1. 具有独立位置的读取和写入,即不需要调用flip()在读取和写入之间切换。
  2. 与java.lang.StringBuilder一样,容量可以按需扩展。
  3. 通过PooledDataBuffer进行缓冲池和引用计数。
  4. 将缓冲区查看为java.nio.ByteBuffer,InputStream或OutputStream。
  5. 确定给定字节的索引或最后一个索引。

8.3 PooledDataBuffer

如Javadoc中ByteBuffer所述,字节缓冲区可以是直接的也可以是非直接的。直接缓冲区可以驻留在Java堆之外,从而无需复制本机I / O操作。这使得直接缓冲区对于通过套接字接收和发送数据特别有用,但是它们的创建和释放也更昂贵,这导致了缓冲池的想法。

PooledDataBuffer是DataBuffer的扩展,可帮助进行引用计数,这对于字节缓冲区池至关重要。它是如何工作的?分配PooledDataBuffer时,引用计数为1。调用keep()会增加计数,而调用release()会减少计数。只要计数大于0,就保证不会释放缓冲区。当计数减少到0时,可以释放池中的缓冲区,这实际上意味着将为缓冲区保留的内存返回到内存池。

请注意,与其直接在PooledDataBuffer上进行操作,不如在大多数情况下,最好使用DataBufferUtils中的便捷方法,仅当它是PooledDataBuffer的实例时才将释放或保留应用于DataBuffer。

8.4 DataBufferUtils

DataBufferUtils提供了许多实用程序方法来对数据缓冲区进行操作:

  1. 将数据缓冲区流合并到单个缓冲区中,可能具有零个副本,例如通过复合缓冲区(如果基础字节缓冲区API支持的话)。
  2. 将InputStream或NIO通道转换为Flux ,反之亦然,将Publisher 转换为OutputStream或NIO通道。
  3. 如果缓冲区是PooledDataBuffer的实例,则释放或保留DataBuffer的方法。
  4. 从字节流中跳过或获取,直到特定的字节数为止。

8.5 Codecs

org.springframework.core.codec包提供以下策略接口:

  1. Encoder,用于将Publisher 编码为数据缓冲区流。
  2. Decoder 将Publisher 解码为更高级别的对象流。

spring-core模块提供byte [],ByteBuffer,DataBuffer,Resource和String编码器和解码器实现。 spring-web模块添加了Jackson JSON,Jackson Smile,JAXB2,Protocol Buffers和其他编码器和解码器。

8.6 Using DataBuffer

使用数据缓冲区时,必须特别小心以确保释放缓冲区,因为它们可能会被合并。我们将使用编解码器来说明其工作原理,但是这些概念会更普遍地应用。让我们看看编解码器必须在内部执行哪些操作来管理数据缓冲区。

在创建更高级别的对象之前,解码器是最后一个读取输入数据缓冲区的对象,因此,它必须按以下方式释放它们:

  1. 如果Decoder只是读取每个输入缓冲区并准备立即释放它,则可以通过DataBufferUtils.release(dataBuffer)这样做。
  2. 如果Decoder使用的是Flux或Mono运算符(例如flatMap,reduce和其他在内部预取和缓存数据项的运算符),或者使用的运算符(例如filter,skip和其他省略项的运算符),则doOnDiscard(PooledDataBuffer.class,DataBufferUtils必须将::: release)添加到组合链中,以确保在丢弃此类缓冲区之前将其释放,这也可能是错误或取消信号的结果。
  3. 如果Decoder以任何其他方式保留一个或多个数据缓冲区,则它必须确保在完全读取时释放它们,或者在读取和释放缓存的数据缓冲区之前发生错误或取消信号的情况下。

请注意,DataBufferUtils#join提供了一种安全有效的方法来将数据缓冲区流聚合到单个数据缓冲区中。同样,skipUntilByteCount和takeUntilByteCount是供解码器使用的其他安全方法。

编码器分配其他人必须读取(和释放)的数据缓冲区。因此,编码器无事可做。但是,如果在使用数据填充缓冲区时发生序列化错误,则编码器必须小心释放数据缓冲区。例如:

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

编码器的使用者负责释放其接收的数据缓冲区。在WebFlux应用程序中,编码器的输出用于写入HTTP服务器响应或客户端HTTP请求,在这种情况下,释放数据缓冲区是代码写入服务器响应或客户端的责任。请求。

参考文献

【https://docs.spring.io/spring-framework/docs/current/reference/html/core.html】【8. Data Buffers and Codecs】

上一篇:BOOST 奇怪的编译错误 boost/asio/detail/consuming_buffers.hpp:105:65: error


下一篇:protocol buffers —— Download Protocol Buffers