単一の受信者に送信するための多くの着信要求を統合するNettyエンドポイントを持つラクダのインスタンスがあります。より具体的には、これはWebサービスであり、着信する各SOAP要求Producer.sendBody()
がラクダのサブシステムになります。各リクエストの処理にはさまざまなルートが含まれますが、それらはすべて単一のNettyエンドポイントにまとめられ、次のレベルのサーバーに送信されます。一度に受信リクエストがほんの一握りである限り、すべて問題ありません。ただし、100を超える同時リクエストが発生し始めると、次の例外が発生します。
java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:71) ~[na:1.6.0_24]
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:209) [na:1.6.0_24]
at org.apache.camel.impl.DefaultServicePool.release(DefaultServicePool.java:95) [camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.impl.ProducerCache$1.done(ProducerCache.java:297) ~[camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.processor.SendProcessor$2$1.done(SendProcessor.java:120) ~[camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.component.netty.handlers.ClientChannelHandler.messageReceived(ClientChannelHandler.java:162) ~[camel-netty-2.9.2.jar:2.9.2]
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) ~[netty-3.3.1.Final.jar:na]
これはDefaultServicePool
、Nettyコンポーネントで使用されているものに由来します。はキューのバックエンドとしてDefaultServicePool
を使用ArrayBlockingQueue
し、デフォルトの容量である100プロデューサーに設定します。パフォーマンス上の理由からサービスプールを使用して、頻繁に再利用されるプロデューサーを作成および破棄し続ける必要をなくします。けっこうだ。残念ながら、私はそれがどのように実装されているかについての論理を理解していません。
これはすべてProducerCache::doInAsyncProducer
、で始まります。これは、を呼び出すことで始まりますdoGetProducer
。このメソッドはacquire
、プールからプロデューサーを試行し、失敗した場合は、を使用して新しいプロデューサーを作成しendpoint.getProducer()
ます。次に、を使用してサービスプールが存在することを確認しますpool.addAndAcquire
。これが完了すると、呼び出し元の関数に戻ります。はdoInAsyncProducer
、完了するまでその処理を実行します。終了した場合は、done
プロセッサを呼び出します。この時点で、交換の処理は完全に完了しているため、次を使用してプロデューサーをプールに解放します。pool.release
ここがゴムが道路にぶつかるところです。このメソッドは、を使用してプロデューサーをバックエンドDefaultServicePool::release
に挿入します。これが私の出身です。ArrayBlockingQueue
add
java.lan.IllegalStateException
なんで?さて、ユースケースを見てみましょう。101の同時着信リクエストがあります。それらのそれぞれは、ほぼ同時にNettyエンドポイントにヒットします。最初に100の容量のサービスプールを作成しますが、開始するには空です。実際、101のリクエストのそれぞれが、endpoint.getProducer
;から新しいプロデューサーを作成します。それぞれが、サービスプール(空)の容量を超えていないことを確認します。そして、それぞれがサーバーに送信し続けます。それぞれが終了した後、それはを実行しようとしますpool.release
。プールの容量に達していないため、最初の100は成功します。101番目のリクエストはキューに追加しようとしますが、キューがいっぱいであるため失敗します。
そうですか?私がそれを正しく読んでいるなら、100を超える同時リクエストがあるときはいつでもこのコードは常に失敗します。私のサービスは10,000以上の同時リクエストをサポートする必要があるので、それはうまくいきません。
より安定した解決策は次のように思われます:
- 初期化時に100個のプロデューサーすべてを事前に割り当てます
acquire
プロデューサーが利用可能になるまでブロックする- ServicePoolを使用している場合は、プール以外のプロデューサーを作成しないでください。
その間、私は着信リクエストを抑制することを考えています。
この質問で私が望んでいるのは、そのロジックを正しく読んでいるかどうかを学び、それが変更される可能性があるかどうかを確認することです。または、私はそれを間違って使用していますか?この種のものを処理するためのより良い方法はありますか?