データバッファとコーデック
Java NIO は ByteBuffer を提供していますが、多くのライブラリは、特にバッファの再利用や直接バッファの使用がパフォーマンス向上に有効なネットワーク操作向けに、独自のバイトバッファ API をそ上に構築しています。たとえば、Netty は ByteBuf 階層構造を持ち、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 bufferEncoder のコンシューマーは、受信したデータバッファーを解放する責任があります。WebFlux アプリケーションでは、Encoder の出力を使用して、HTTP サーバーレスポンスまたはクライアント HTTP リクエストに書き込みます。この場合、データバッファーの解放は、サーバーレスポンスまたはクライアントへのコード書き込みの責任です。リクエスト。
Netty で実行する場合、バッファリークのトラブルシューティング [GitHub] (英語) 用のデバッグオプションがあることに注意してください。