データバッファとコーデック
Java NIO は ByteBuffer
を提供しますが、多くのライブラリは、特にバッファの再利用や直接バッファの使用がパフォーマンスに有益であるネットワーク操作のために、独自のバイトバッファ API を構築します。たとえば、Netty には ByteBuf
階層があり、Undertow は XNIO を使用し、Jetty は解放されるコールバックとともにプールされたバイトバッファーを使用します。spring-core
モジュールは、次のようにさまざまなバイトバッファ API を操作するための一連の抽象化を提供します。
DataBufferFactory
は、データバッファーの作成を抽象化します。DataBuffer
は、プールできるバイトバッファを表します。DataBufferUtils
は、データバッファ用のユーティリティメソッドを提供します。コーデックは、データバッファストリームをより高いレベルのオブジェクトにデコードまたはエンコードします。
DataBufferFactory
DataBufferFactory
は、次の 2 つの方法のいずれかでデータバッファを作成するために使用されます。
DataBuffer
の実装は要求に応じて拡大および縮小できますが、既知の場合は事前に容量を指定するオプションで、新しいデータバッファーを割り当てます。既存の
byte[]
またはjava.nio.ByteBuffer
をラップします。これにより、指定されたデータがDataBuffer
実装で装飾され、割り当ては行われません。
WebFlux アプリケーションは、DataBufferFactory
を直接作成するのではなく、クライアント側の ServerHttpResponse
または ClientHttpRequest
を介してアクセスすることに注意してください。ファクトリの型は、基盤となるクライアントまたはサーバーによって異なります。たとえば、Reactor、Netty の場合は NettyDataBufferFactory
、その他の場合は DefaultDataBufferFactory
です。
DataBuffer
DataBuffer
インターフェースは java.nio.ByteBuffer
と同様の操作を提供しますが、Netty ByteBuf
に触発されたいくつかの追加の利点ももたらします。以下は、利点の一部のリストです。
独立した位置での読み取りと書き込み、つまり読み取りと書き込みを交互に行うために
flip()
を呼び出す必要はありません。java.lang.StringBuilder
と同様に、要求に応じて容量が拡張されました。プールされたバッファーと
PooledDataBuffer
を介した参照カウント。バッファを
java.nio.ByteBuffer
、InputStream
、OutputStream
として表示します。特定のバイトのインデックスまたは最後のインデックスを決定します。
PooledDataBuffer
ByteBuffer (標準 Javadoc) の Javadoc に従って、バイトバッファーは直接または非直接にできます。ダイレクトバッファは Java ヒープの外側に存在する場合があり、ネイティブ I/O 操作のためにコピーする必要がなくなります。これにより、直接バッファはソケットを介してデータを送受信するのに特に役立ちますが、作成および解放するのに費用がかかるため、バッファをプールするという考えにつながります。
PooledDataBuffer
は DataBuffer
の拡張であり、バイトバッファプーリングに不可欠な参照カウントを支援します。どのように機能しますか? PooledDataBuffer
が割り当てられると、参照カウントは 1 になります。retain()
を呼び出すとカウントが増加し、release()
を呼び出すとカウントが減少します。カウントが 0 を超えている限り、バッファは解放されないことが保証されます。カウントが 0 に減少すると、プールされたバッファを解放できます。実際には、バッファの予約メモリがメモリプールに戻される可能性があります。
PooledDataBuffer
を直接操作する代わりに、ほとんどの場合、PooledDataBuffer
のインスタンスである場合にのみ DataBuffer
にリリースまたは保持を適用する DataBufferUtils
の便利なメソッドを使用することをお勧めします。
DataBufferUtils
DataBufferUtils
は、データバッファを操作するためのユーティリティメソッドをいくつか提供します。
基礎となるバイトバッファー API でサポートされている場合は、複合バッファーなどを使用して、データバッファーのストリームをゼロコピーで単一のバッファーに結合します。
InputStream
または NIOChannel
をFlux<DataBuffer>
に、またはその逆にPublisher<DataBuffer>
をOutputStream
または NIOChannel
に変えます。バッファーが
PooledDataBuffer
のインスタンスである場合、DataBuffer
を解放または保持するメソッド。特定のバイトカウントまでバイトストリームからスキップまたは取得します。
コーデック
org.springframework.core.codec
パッケージは、次の戦略インターフェースを提供します。
Publisher<T>
をデータバッファのストリームにエンコードするEncoder
。Decoder
は、Publisher<DataBuffer>
をより高いレベルのオブジェクトのストリームにデコードします。
spring-core
モジュールは、byte[]
、ByteBuffer
、DataBuffer
、Resource
、String
エンコーダーおよびデコーダーの実装を提供します。spring-web
モジュールは、Jackson JSON、Jackson Smile、JAXB2、Protocol Buffers、その他のエンコーダーとデコーダーを追加します。WebFlux セクションのコーデックを参照してください。
DataBuffer
を使用する
データバッファーを使用する場合、バッファーがプールされる可能性があるため、バッファーが解放されるように特に注意する必要があります。コーデックを使用してその仕組みを説明しますが、概念はより一般的に適用されます。データバッファを管理するためにコーデックが内部的に行う必要があるものを見てみましょう。
Decoder
は、より高いレベルのオブジェクトを作成する前に、入力データバッファーを最後に読み取るため、次のように解放する必要があります。
Decoder
が単に各入力バッファーを読み取り、すぐにそれを解放する準備ができている場合、DataBufferUtils.release(dataBuffer)
を介して解放できます。Decoder
がFlux
またはMono
演算子(flatMap
、reduce
など)を使用してデータ項目を内部でプリフェッチおよびキャッシュする場合、またはfilter
、skip
などの演算子を使用して項目を除外する場合は、doOnDiscard(DataBuffer.class, DataBufferUtils::release)
をコンポジションに追加する必要があります。チェーンは、おそらくエラーまたはキャンセルシグナルの結果として、そのようなバッファーが破棄される前に解放されることを保証します。Decoder
が他の方法で 1 つ以上のデータバッファを保持している場合、完全に読み取られたとき、キャッシュされたデータバッファが読み取られて解放される前にエラーまたはキャンセルシグナルが発生した場合に、それらが解放されることを確認する必要があります。
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
Encoder
のコンシューマーは、受信したデータバッファーを解放する責任があります。WebFlux アプリケーションでは、Encoder
の出力を使用して、HTTP サーバーレスポンスまたはクライアント HTTP リクエストに書き込みます。この場合、データバッファーの解放は、サーバーレスポンスまたはクライアントへのコード書き込みの責任です。リクエスト。
Netty で実行する場合、バッファリークのトラブルシューティング [GitHub] (英語) 用のデバッグオプションがあることに注意してください。