コンテクスト:
当社では、中規模 (数百から数千台のサーバー) のデータセンター全体のメッセージングに RabbitMQ 2.8.4 HA クラスターを使用しています。メッセージング機能は当社のアプリにとって非常に重要です。
AMQP/RabbitMQ 操作には C クライアント API を使用し、RabbitMQ 管理プラグインには HTTP API を使用して、キューの状態を定期的に監視し、サイズやスループットなどに基づいてアラートを送信します。
クライアントからメッセージング サーバーに許可されるポート/接続は、AMQP ポートと管理 API の HTTP のみです。
近い将来、経営陣はメッセージング クラスターのダウンタイム (これはアプリの特定の要求部分のダウンタイムにつながります) は許容できないと判断しました。数か月後には可能になりますが、それまでは販売主導の懸念により、メッセージング システムのメンテナンスを行うことができません。残念ですが、私たち (インフラストラクチャ/DevOps 担当者) には、そのような決定を下す権限がありません。
問題:
しばらくの間、私たちは、このSOの質問. 接続ストームの後、または新しいクライアントが長期間にわたって AMQP システムに頻繁に接続して使用すると、RabbitMQ 管理 API は、もはや存在しない「実行中」としてリストされている数十万の接続を表示します。netstat
、lsof
およびその他の診断により、これらの接続に対応するソケットがクライアント上で開いていないことが証明されます。管理 API は、何らかの理由でエントリを整理することができません。 は、rabbitmqctl list_connections
存在しない接続の一部がまだ開いていることを表示することがありますが、常に管理 API よりもはるかに少ない数が表示され、その「古さ」検出は API よりも優れているようで、最終的にはリストから誤った接続を削除します。
Web UI 経由でこれらの「偽の」接続の 1 つを削除しようとすると、ページは「削除」を押しても応答せず、接続はリストに残ります。JSON API で DELETE を送信して削除しようとすると、接続が有効ではないことを示す 500 サーバー エラーが発生し、削除できません (上記のリンクされた SO の質問で説明されているとおりです)。
これらの存在しない接続オブジェクトが長期間蓄積されると、順不同で 3 つの悪いことが起こります。
- RabbitMQ サーバーでは、メモリの急上昇が大量に発生します (突然発生します。管理 API の接続速度低下とは関係ありませんが、時間の経過とともにメモリ消費量は徐々に増加します)。これにより、クラッシュが発生することがよくあります。
- 管理 API がリクエストへの応答を停止します (API クライアントでタイムアウトになります)。
- RabbitMQ サーバー自体は、HTTP/管理 API を使用して何も実行していない AMQP クライアントからの接続を散発的に拒否し始めます。
この問題によってアプリが動作しなくなった場合は、次の 2 つのいずれかを実行する必要があります。 * メッセージング クラスター全体を再起動します。これは、一時的なダウンタイムが発生するため好ましくありません。また、このプロセス中にメッセージがドロップされる可能性があるため好ましくありません。 * 管理 API のデータベースを再起動して消去します。これは、管理 API を使用できなくなるため好ましくありません。さらに悪いことに、最初の箇条書きのようにクラスター全体を再起動するまで管理 API が正常に起動しなくなることが多いため好ましくありません。
同様の問題に悩まされているほとんどの人は、RabbitMQ 3.* にアップグレードすることで問題を解決しています。私たちには、このようなプロジェクトを行う時間も、許可も、ダウンタイムの許可もありません。この問題が原因でクラスターを再起動すると、定期的に数分間の停止が発生しますが、アップグレードする許可はありませんでした。これは完全に政治的な制約ですが、残念な現実です。
質問:
近い将来に RabbitMQ をアップグレードできず、管理 API を引き続き使用する必要があることを考えると、メッセージング クラスターの障害を引き起こす永続的で古い接続の問題をどのように解決できるでしょうか。
rabbitmqctl
新しい/異なるクライアント コードを開発し、メッセージング サーバー自体でジョブを実行する機能はありますが、ローカルまたはssh
クライアント マシン経由で使用することはできません。
私たちが試したこと:
- より優れたクライアントが自らクリーンアップし、古い接続を残さないことを期待して、AMQP クライアント ライブラリを切り替えようとしましたが、効果はありませんでした。問題は解決しません。
- RabbitMQ サーバー自体の
tcp_keepalive
およびTCP 設定を試してみました。これらの値の組み合わせでは問題は解決されません。exit_on_close
- JSON API の接続リストから返されたすべての接続を反復処理し、使用中に必要な時間よりも長く開いている各接続に対して DELETE を送信するスクリプトを作成しました。このようなリクエストはすべて、上記の SO の質問で説明されているように、500 サーバー エラーで満たされます。
答え1
定期的にすべてを再起動し続けます。
もう 1 つのオプションは、3.x から現在のバージョンに修正をバックポートすることですが、アップグレードが許可されていない場合は、おそらくこれも許可されません。また、特に RabbitMQ コードが大幅にリファクタリングされている場合は、アップグレードよりもはるかに多くの作業が必要になることはほぼ間違いありません。
変更できるようになるまで政治状況に応じて、これらが選択肢となります。