2

ChannelFutureでクライアント側のsync()を待たずに、制御されていない量のTextWebSocketFramesを単純なNetty WebSocketエコーサーバー実装(サンプルパッケージにある実装のわずかに変更されたバージョン)に送信すると、サーバーのヒープメモリ使用量が増加します最終的にメモリがなくなるまで指数関数的に。

テストケース:クライアントは、実際のバイトが書き込まれるまで待機しません。また、サーバーが次のテキストフレームを書き込む前に、テキストをクライアントに「エコー」するのを待機しません。

for (int i = 0; i < 10000000; i++) {
    ch.write(new TextWebSocketFrame("Message #" + i));
}

yourkitメモリプロファイラーを観察すると(約20.000フレームが書き込まれたテストの15秒後)、BigEndianHeapChannelBufferオブジェクトの数が過度に増加しました。

BigEndianHeapChannelBuffer 284,509 (objects) 9,104,288 (shallow size) (after ~30.000 frames sent within 10 second window)

サーバー側では、主にBigEndianHeapChannelBuffersオブジェクトとCompositeChannelBuffersオブジェクトから大きな山が見られますが、これらのオブジェクトはクリーンアップもガベージコレクションも行われません(参照が保持されているため不可能な場合があります)。これは、(単一の)ワーカースレッドがクライアントからの急速に着信する要求の処理でビジーであるために、クライアントチャネルへのダウンストリームの「エコー」応答を書き込めないことに関係していると思います。

サーバー側でこれ(偶発的なサービス拒否)を防止/抑制する方法はありますか?

4

1 に答える 1

4

Nettyでは、ほとんどのI/O操作は非同期です。したがって、前の書き込みが終了するのを待たずに何千ものメッセージを書き込むと、が得られますOutOfMemoryError。それを避けるために、保留中の書き込みの数をカウントするためのカウンター変数を使用することをお勧めします。例えば:

private final AtomicInteger pendingWrites = new AtomicInteger();

...
while (pendingWrites.get() < MAX_PENDING_WRITES) {
  pendingWrites.incrementAndGet();
  ch.write(msg).addListener(new ChannelFutureListener() {
    ...
    pendingWrites.decrementAndGet();
    if (pendingWrites.get() < MAX_PENDING_WRITES) {
      // resume writing here
    }
  }
}

ChunkedWriteHandlerまたは、基本的に同じ仕事をするものを使用することもできます。

于 2012-06-15T01:40:39.683 に答える