1

1 つの RabbitMQ 接続、10K の公開チャネル、10K の消費チャネル、および 100 のスレッドがあります。すべてのチャネルは、排他的なキューと外部同期を使用して、パブリッシュ/コンシュームの高速な「ループ」に入っています。パブリッシュ、コンシューム、またはクローズがチャネルで同時に発生する可能性はありません。100 個のスレッドでチャネルを同時に閉じると、最初の 100 個のチャネルごとに 2.5 秒の遅延が発生します。後続のすべてのクロージャは 0.5 秒未満で処理されます。20K + 20K チャンネルまで上げると、最初の 100 チャンネルを閉じるのに 5 秒かかります。30K+30K の場合、それぞれ 10 秒かかります。これは、接続の最大チャネルにほぼ達しています。同様に、スレッド数を 50 に減らすと、最初の 50 チャネルがゆっくり閉じます。

もう一度言います... 30K + 30K チャネルでは、最初の 100 はそれぞれ 100 の同時スレッドを使用して閉じるのに 10 秒かかります。残りの 59,900 は、それぞれ 0.5 秒未満かかります。私の感じでは、接続全体で奇妙な同期/継続が行われているようです。100 のスレッドで同時に 100 のチャネル クローズ リクエストで接続を確立すると、ボークします。最初のけいれんの原因が何であれ、その後のクローズ操作には影響がないようで、すべてが順調に進んでいます。

プレッシャーを和らげるために、接続の数を増やしてみました。これは確かに予測可能な結果で機能します。30K+30K の接続が 2 つの接続に均等に分散されると、最初の 100 のチャネル クローズ遅延が 10 秒から 5 秒に半分になります。10 の接続を使用すると、その後の 59,900 のチャネル閉鎖と比較すると、遅延は感知できません。50K+50K チャネルにジャンプすると (10 接続でこれを行うことができますが、チャネル最大のため 1 接続ではできません)、遅延が再び忍び寄り始めます。

このアプローチに関する私の懸念は、1) ドキュメントでは I/O リソースのオーバーヘッドが原因で複数の接続が推奨されていないこと、および 2) 接続ごとの最適なチャネル数を賢明に予測する方法がアプリケーションにとって明確でないことです。接続ごとのチャネル数にソフトな制限がある場合、それが文書化されていないか、API で利用可能にされていないのはなぜですか? 多数の接続を管理し、それらの接続全体に堅牢な方法でチャネルを割り当てる必要がある場合、クライアント ライブラリの作業を行っているように感じます!

私は、RabbitMQ サーバーからのチャネル終了確認を待たずに、クライアント ライブラリを変更しようとしました。これは魅力のように機能しました。チャネルはクライアントで遅延なく即座に閉じられ、サーバーで閉じられたことが確認されました。しかし 8 時間後、クライアント ライブラリ内部のチャネル リソースが解放されなかったため、ヒープ領域が不足していました。遅延の原因を突き止めることはできませんでした...それはクライアント ライブラリにありますか、それともサーバー自体にありますか? さらに先に進むには、ワイヤ プロトコルをトレースする必要があります。私はこのアプローチで軌道に乗っていないと思います!

質問:

アプリケーションを変更する前に、これが Java クライアントの既知の問題かどうかを知りたいですか? 複数の接続とアプリケーション レベルのチャネル管理よりも優れた回避策はありますか? 実際には、私の実際のアプリケーションはプロセスごとに約 20,000 チャネルを使用しますが、これは過剰だとは感じません。ルーティングに RabbitMQ をさらに活用しているため、メッセージのスループットは実際にはかなり軽いです。遅延に耐えることさえできますが、遅延はすでに顕著です。より少ないチャネルを使用するようにリファクタリングすることはできますが、そうするとチャネルを共有することになり、それらの使用法を同期するか、ドキュメントのガイドラインを無視する必要があります。ただし、エラー処理パラダイムにより、これは面倒になります。チャネル エラーが発生すると終了するため、エラーを分離し、浸透を防ぎ、堅牢な方法で回復することは困難です。

詳細:

RabbitMQ サーバー 3.1.5、Java amqp クライアント 3.1.5。また、同じテスト ハーネスで 3.1.4、3.1.3 を試してみたところ、3.0 とそれ以前の運用アプリケーションでまったく同じ動作であると思われることがわかりました。

私のテスト ハーネスはスタンドアロンですが、ここに投稿するには少しわかりにくいです。私は 10,000 個のテスト クライアントを作成し、それぞれが排他的なキューを作成し、継続的な発行 - 消費 - 発行 - 消費ループを実行します。パブリッシャーとコンシューマーはそれぞれ独自のチャネルを持っています。私は Channel.basicConsume を DefaultConsumers とデフォルトのコンシューマー エグゼキューター サービスと共に使用しています。また、デフォルトの実装と同じように見える独自の Java エグゼキューター サービスを使用しましたが、さまざまな数のスレッドがあり、目に見える効果はありませんでした。

handleDelivery で消費されるメッセージは、java.concurrent の固定スレッド プールを介して新しいパブリッシュ タスクを生成するため、コンシューマ スレッドはチャネル操作で拘束されたり使用されたりすることはありません。チャネルのクローズは同期されるため、パブリッシュと同時に発生することはありません。ただし、クローズの処理中に消費が発生するのを止めるものは何もありません-コンシューマーは私の制御外です。

ご指摘ありがとうございます。

4

0 に答える 0