データベース
 Computer >> コンピューター >  >> プログラミング >> データベース

HARedisデプロイメントでの信頼性の高いPUBSUBとブロッキングリストの操作

HARedisデプロイメントでの信頼性の高いPUBSUBとブロッキングリストの操作

標準の冗長Redisソリューションは、フェイルオーバーを管理するSentinelを使用してマスター/スレーブレプリケーションを実行することです。これは、a)クライアントサポートとSentinelを使用して現在のマスターを検出するかb)Sentinelによって管理されてマスターを指すRedisポッドの前にあるTCPプロキシのいずれかでフォローアップされることが期待されます。前者はRedisSentinelの設計方法であり、後者は成長傾向であり、ObjectRocketRedisの構成方法です。

Redisのコマンドに対するフェイルオーバーの影響

Redis操作の大部分では、これは期待どおりに機能します。フェイルオーバー時に、クライアントコードは接続が失敗したことを検出し、a)現在のマスターを検索して再接続するか、b)上記のシナリオに基づいて再接続を試みます。プロキシベースのシナリオもRedisサーバーの再起動と同じであるため、これは、再起動可能な単一インスタンスのRedisがある場合にも当てはまります。

ただし、少数のRedisコマンドは、長期的なブロッキングまたは非アトミックです。 PUBSUBsubscribeコマンドとpsubscribeコマンドは、特定のチャネルに送信されるメッセージを送信する要求をコマンドが登録するという点で非アトミックですが、実際のデータははるかに遅れて到着する可能性があります。公開後。 BL *コマンドは、サブスクライブコマンドと同様に、ブロックされ、削除するリストでアイテムが使用可能になるまで戻らないという点で、長期的な2フェーズコマンドです。

これらのコマンドの主な共通点は、コマンドが発行され、サーバー上で登録の形式が実行されることですが、データは後で取得される可能性があります。発行されているコマンドと、フェイルオーバーおよび再起動で応答するデータを持つサーバーとの間に発生する可能性があります。再起動またはフェイルオーバーの場合、「登録」はサーバー上に存在しなくなります。これにより、これらのシナリオでこれらのコマンドに関して何が起こるかという疑問が生じます。

これらの詳細を掘り下げる前に、テスト中に機能し、結果と障害シナリオのコーディング方法に劇的な影響を与えるため、もう1つの側面を取り上げます。実際、フェイルオーバーには2つのタイプがあります。私たちのほとんどが考えるフェイルオーバーがあります-マスターの喪失。このタイプのフェイルオーバーでは、マスターは応答しません。これを「トリガーフェイルオーバー」と呼びます。私が「開始フェイルオーバー」と呼ぶ2番目のクラスのフェイルオーバー。

開始されたフェイルオーバーは、管理者がセンチネルフェイルオーバーコマンドをセンチネルに送信したときに発生します。センチネルはポッドを再構成し、マスターを昇格させてから元のマスターを降格します。表面上、これら2つのシナリオは同じように見えます。大多数のRedisコマンドの場合、それらは同じように扱うことができます。ただし、ロングブロックコマンドの場合は、独自のコンテキストで理解する必要があります。

これら2つのフェイルオーバークラスの主な機能上の差別化要因は、イベントのシーケンスです。トリガーされたフェイルオーバーが発生するのは、マスターが応答していないためです。その場合、データは追加または変更されず、マスターにメッセージは公開されません。開始されたフェイルオーバーでは、2つのマスターが存在する非常に短いウィンドウがあります。

ここで説明する手法とシナリオは、一般的に説明され、Python Redisライブラリ「redis-py」を使用して示されますが、すべてのクライアントライブラリに適用されます。背景を見て、これがロングブロックコマンドの使用にどのように影響するかを見てみましょう。まず、PUBSUBを見てみましょう。

PUBSUB

Redis PUBSUBは、PUBLISH、SUBSCRIBE、およびPSUBSCRIBEの3つのコマンドで構成されています。最初のコマンドはメッセージの送信に使用され、後の2つはPUBLISHコマンドを介して公開されたメッセージの登録と受信に使用されます。まず、PUBLISHコマンドで何が起こるかを調べてみましょう。

PUBLISHを実行すると、メッセージはすぐに指定されたチャネルに公開され、メッセージを受信したクライアントの数が返されます。障害シナリオはどのようにしてこれを適切なものにしますか?フェイルオーバーの場合を考えてみましょう。 TCP接続がなくなると、クライアントは再接続する必要があります。プロキシと通信する場合は、直接再接続して別のサーバーを取得します。 Sentinelディスカバリーを実行する場合、少し時間がかかりますが、それでも別のサーバーに接続します。問題はタイミングにあります。サブスクライバーが接続する前にパブリッシャーが新しいサーバーに接続すると、メッセージは失われ、失われます。しかし、それは起こり得ますか? SUBSCRIBEコマンドを参照していることを理解するため。

コードがサブスクライブコマンドを発行すると、サーバーは、その特定の接続がサブスクライブされたチャネルでメッセージを取得する必要があるメモリ内のデータ構造に登録します。この情報はスレーブに伝達されません。したがって、障害が発生してクライアントが再接続した場合は、subscribeコマンドを再発行する必要があります。そうしないと、新しいサーバーは、特定の接続が特定のメッセージを受信する必要があることを認識しません。これで、サブスクライバーがパブリッシャーの後に新しいマスターに再接続できるという明確な可能性があります。メッセージが失われる条件が存在します。では、これを最小化または防止する方法は?いくつかのオプションがあります。

まず、発行者は、PUBLISHがそれを受信したクライアントの数を返すという事実を活用できます。ゼロクライアントがそれを受け取った場合、パブリッシャーは再試行できます。これが静的な加入者数であるシステム、または「少なくとも1つの加入者」で十分なシステムの場合、これによりメッセージの損失を防ぐことができます。この基準を満たさないシステムには、堅牢性が低い場合でも別のオプションがあります。

2番目のオプションは、再接続ウィンドウを制御することです。ここでの経験則は、パブリッシャーの遅延をサブスクライバーの遅延の少なくとも3倍にすることです。これにより、メッセージを公開する前に、サブスクライバーが再接続する機会が少なくとも3つ提供されます。サブスクライバーが最初にオンラインである限り、メッセージはエーテルに入りません。ただし、制御しないよりも堅牢ですが、メッセージが失われる可能性があります。

この競合状態を緩和するために提示する3番目のオプションは、ロックメカニズムを組み込むことです。これは確かに最も複雑なルートです。クライアントが接続するまで発行者が発行できないようにするメカニズムを構築または採用する必要があるためです。基本的なプロセスは、サブスクライバーが受信の準備ができていることを登録し、サブスクライバーがメッセージを公開する前に有効で準備ができているサブスクライバーを確認する、何らかの形式の共有ロケーション(Zookeeper、データベース、領事館など)を用意することです。広告をさらに複雑にするために、加入者はTCP接続を失ったときに、このメカニズムで自分自身を「設定解除」する必要があります。

これはすべて、トリガーされたフェイルオーバーのためのものであることに注意する必要があります。マスターが応答しないため、メッセージが送信されないため、機能します。開始されたフェイルオーバーシナリオでは、マスターは、マスターでなくなるか切断されるまで、メッセージを受け入れ続けます。上記の方法のうち、このシナリオを適切かつ完全に捉えることはできません。

フェイルオーバーが開始された場合(たとえば、システムメンテナンス中に発生する可能性があります)、最善の強化効果は、最初と2番目のオプションの両方を使用し、メッセージが失われる可能性があることを受け入れることです。メッセージの損失の量は、公開スケジュールに完全に依存します。平均して数分ごとにメッセージを公開する場合、実際に損失ウィンドウに遭遇する可能性はごくわずかです。公開間隔が大きいほど、リスクは小さくなります。逆に、毎秒数百または数千のメッセージを公開している場合は、確かにいくつかを失うことになります。マネージドプロキシのシナリオでは、プロキシでフェイルオーバーが完了する速度に依存します。 ObjectRocketのRedisプラットフォームの場合、このウィンドウは最大1.5秒です。

したがって、制限で失敗する間隔の1つのアプローチは、サブスクライバーに公開間隔の3分の1の間隔で再接続を試行させることです。したがって、メッセージを1分に1回公開する場合は、少なくとも20秒ごとに再接続するようにサブスクライバーをコーディング/構成し、1〜2分ごとにパブリッシャーを再接続します。公開間隔が3秒のマークに近づくと、フェイルオーバーの再接続プロセス(ルックアップ、TCPハンドシェイク、AUTH、SUBSCRIBEなど)が簡単に合計数秒になる可能性があるため、これを達成するのは難しくなります。

BLPOPとその仲間たち

これにより、これらの条件下でブロッキングリストコマンドがどのように動作するかが問題になります。ここでのニュースは、公開するよりも優れています。この場合、複製されるデータ構造を変更しています。さらに、これらはデータ構造の変更であるため、再接続の順序が原因でメッセージが失われるリスクについては、同じレベルではありません。プロデューサーがサブスクライバーの前に再接続した場合、期待される動作に変更はありません。プロデューサーはアイテムをリスト(またはPOPとPUSH)にプッシュし、コンシューマーが接続するとデータがそこに表示されます。 1つの例外を除いて。

フェイルオーバーが開始された場合、簡単なマルチマスターの問題を考慮する必要があります。しばらくの間、ミリ秒のオーダーで、開始されたフェイルオーバー中の元のマスターは引き続きデータ変更を受け入れ、スレーブはすでにマスターにプロモートされているため、これらの変更は複製されません。このウィンドウは非常に小さいことに注意してください。テストでは、1桁のミリ秒のオーダーですが、そこにあります。公開と同様に、これは基本的にコーナー条件であり、たとえば、古いマスターにPOPコマンドを発行する確率はまれですが、リスト変更コマンドを実行するプロデューサーとコンシューマーの割合が1秒あたり数千に達すると、考えられる条件になります。

たとえば、BRPOPLPUSHを実行しているワーカーの場合、フェイルオーバー後に移動中のアイテムが元の位置に「戻る」という結果になります。 BLPOPの場合、結果は基本的に同じになります。フェイルオーバーが完了すると、アイテムは再キューイングされているように見えます。アイテムがべき等のジョブである場合、これは問題にはなりません。べき等でないジョブの場合にこれを説明するためにどの程度の防御コーディングを行う必要があるかは、変更を行う頻度とこれに遭遇する可能性を考慮して、実行中のジョブまたはアイテムが2回処理された効果を判断した結果です。状況。また、これはフェイルオーバーが開始された場合にのみ発生するため、これは運用管理下にある必要があります。可能な限り、システムの使用率を最小限に抑えたり、さらには最小限に抑えたりするときに、メンテナンスを実行することをお勧めします。この可能性を排除します。

トリガーされたフェイルオーバーの場合、データが失われることはありません。トリガーされたフェイルオーバーは、マスターが応答しないときに発生するため、マスターに変更は加えられていません。ただし、これらの各シナリオと同様に、実際のTCP再接続を処理する問題があります。これは、フェイルオーバーシナリオでは必須です。

クライアントの再接続

トリガーまたは開始されたフェイルオーバーシナリオでは、クライアントは、Sentinelルックアップまたは同じアドレスへの短いウィンドウのいずれかの後、検出して再接続する必要があります。マネージドプロキシレイヤーを利用するObjectRocketRedisの場合、クライアントは単に再接続します。実際のフェイルオーバープロセス自体は、完了するまでに最大2秒かかる場合があります。したがって、クライアントコードはこれを説明する必要があります。理想的には、接続がルートのどこかで単に切断されたネットワークブリップを処理するために、すぐに再試行する必要があります。ただし、これには、サーバーの再起動などの場合を考慮して再試行するバックアルゴリズムを伴う必要があります(スタンドアロンのRedisの再起動またはプロキシの再起動のいずれかで発生します)。

再接続時に、要求チャネルと新しいTCP接続の間のリンクを確立する必要があるため、サブスクライバーは再サブスクライブする必要があります。 RedisのPUBSUBチャネルは、サブスクライバーまたはパブリッシャーがアクセスしようとしたときに作成されるため、チャネルを「再作成」する必要はありません。

これにより、これを行う方法がわかります。答えは、使用中のクライアントライブラリとそれが切断を処理する方法に大きく依存します。理想的なシナリオでは、接続は自動再試行用に構成可能であり、境界に達した場合は最終的な障害が返されます。これまで、いくつかのライブラリをテストして、これをどのように処理するかをテストしましたが、劇的に異なります。ここでは、かなり一般的なもの、redis-pyについて説明します。

良いニュースの最初のビットは、接続が切断されたときにredis-pyが再試行するように見えることです。残念ながら、すぐに再試行するため、フェイルオーバー中に接続を確実に回復するには速すぎます。さらに、これを構成する方法はないようです。その結果、コードは失敗した再接続の試行をキャッチ/検出し、再接続を自分で管理する必要があります。

まず、いくつかの標準的なredis-pyパブリッシュおよびサブスクライバーコードブロックを調べてみましょう。

### Publisher example code
r.publish(channel,message)

### Subscriber example code
p = r.pubsub()
p.subscribe(channel)
for item in p.listen():
   # do stuff with message (item)

これはかなり簡単です。ただし、サブスクライバーのforループ中の即時再試行が失敗すると、redis.ConnectionError例外がスローされます。トリッキーな点は、p.listen()行のforアイテムの「内部」で発生することです。したがって、それを適切にキャッチするには、forステートメント全体をtry/exceptブロックでラップする必要があります。これはせいぜい問題があり、不必要なコードの複雑さにつながります。

別のルートは、代わりに次のことを行うことです。

### Publisher example code
p = r.pubsub()
p.subscribe(channel)
while True:
    try:
        message = p.get_message()
    except redis.ConnectionError:
        # Do reconnection attempts here such as sleeping and retrying
        p = r.pubsub()
        p.subscribe(channel)
    if message:
        # do something with the message
    time.sleep(0.001)  # be nice to the system :)

このメソッドを使用して、get_message()を直接呼び出します。これにより、その時点で例外をキャッチし、「p」オブジェクトの接続を再確立できます。スリープする時間は、あるとしても、コードの要件によって異なります。もちろん、サブスクライバーが特定の数のメッセージを処理することを期待していて、forループがより適切に機能する場合でも、それは機能します。パブリッシャーの場合、コードは通常イテレーターで実行されないため、より単純です。

### Publisher example code
while True:
    try:
       rcvd = r.publish(channel,message)
       if rcvd >0:
          break
    except redis.ConnectionError:
       # handle reconnect attempts

この方法を使用すると、再試行するかどうか、いつ、どのくらいの頻度で再試行するかを制御できます。これは、障害イベントを適切に透過的に処理するために重要です。コードがSentinelを直接使用している場合、または問題のRedisサーバーが再起動した場合でも、これは必要なことと同じであることに注意してください。そのため、実際にはすべての公開コードはこの基本的なメカニズムに従う必要があります。

BLPOPなどのブロッキングリストコマンドの場合、データが保持されるため、クライアントの再接続の順序は厳密には重要ではありません。コマンドの実行により「redis.ConnectionError」例外がスローされた場合に接続を再確立するには、上記のtry/exceptメソッドが必要になります。

特にObjectRocketRedisプラットフォームに対するredis-pyの場合、3秒を含む再試行ウィンドウでこれらの手法を使用すると、トリガーされたフェイルオーバーでデータ損失が発生することはなく、フェイルオーバーの開始時にノンストップパブリッシャーで約1.5秒のメッセージ損失が発生する可能性があります。インスタンスの垂直方向のサイズ変更中など。

コード例はredis-pyに固有のものですが、基本的な手法は、サーバーの再接続を処理する必要があり、PUBSUBコマンドまたはブロッキングリスト操作のいずれかを使用するクライアントコードで使用する必要があります。この知識を身に付ければ、これらの手法を実装して、可用性の高いRedisポッドを使用し、アプリケーションが深夜に運用チームを起こさずにフェイルオーバーを処理できることを知ることができます。


  1. Discord コマンド一覧

    ゲーマーは、Mumble、Steam、TeamSpeak などのさまざまな種類のチャット アプリケーションを使用して、ゲームプレイ中に通信します。オンラインゲームが好きな方ならご存知かもしれません。最近最も使用されているトレンディなチャット アプリの 1 つが Discord です。 Discord を使用すると、プライベート サーバーを介して他のオンライン プレイヤーと音声またはビデオ チャットやテキスト メッセージを送信できます。複数の Discord コマンドがあります 、サーバーに入力して、効率を改善し、チャンネルを管理し、多くの楽しみを持つことができます.これらは、Discord ボ

  2. Windows 11 実行コマンドの完全なリスト

    実行ダイアログ ボックスは、熱心な Windows ユーザーにとってお気に入りのユーティリティの 1 つです。これは Windows 95 から存在し、長年にわたって Windows ユーザー エクスペリエンスの重要な部分となっています。その唯一の義務はアプリやその他のツールをすばやく開くことですが、TechCult の私たちのような多くのパワー ユーザーは、[実行] ダイアログ ボックスの便利な性質を気に入っています。コマンドを知っている限り、どのツール、設定、またはアプリにもアクセスできるため、プロのように Windows を簡単に操作できるチート シートを提供することにしました。ただし、