Redisがキューイングシステムを実装するための良い候補になり得るという印象を受けています。ここまでは、MySQLデータベースとポーリング、またはRabbitMQを使用してきました。 RabbitMQには多くの問題がありました。クライアントライブラリは非常に貧弱でバグが多く、それらを修正するために多くの開発時間を費やしたり、サーバー管理コンソールでいくつかの問題を発生させたりしたくありません。少なくとも、システムがキューをインテリジェントにサポートするアーキテクチャを備えている限り、私たちはおそらくミリ秒を把握していないか、パフォーマンスを大幅に押し上げているわけではありません。
それが背景です。基本的に、私は非常に古典的で単純なキューモデルを持っています。仕事を生み出す複数のプロデューサーと仕事を消費する複数のコンシューマーがあり、プロデューサーとコンシューマーの両方がインテリジェントにスケーリングできる必要があります。素朴なPUBSUB
が機能しないことがわかりました。私はallサブスクライバーに作業を消費させたくないので、-oneサブスクライバーに作業。最初は、BRPOPLPUSH
はインテリジェントなデザインのように見えます。
BRPOPLPUSH
の基本的な設計は、1つのワークキューと1つのプログレスキューがあることです。コンシューマーは作業を受け取ると、アイテムをプログレスキューにアトミックにプッシュし、作業が完了するとLREM
です。これにより、クライアントが死亡した場合の作業のブラックホール化が防止され、監視がかなり楽になります。たとえば、大量のタスクがあるかどうかを通知するだけでなく、コンシューマーがタスクの実行に長い時間を要する問題があるかどうかを通知できます。
それは保証します
PUBSUB
を使用していないのは不思議に思えます。これは、Redisのキューイングに関するほとんどのブログ投稿が焦点を当てているものだからです。だから私は明らかな何かを見逃しているように感じます。タスクを2回消費することなくPUBSUB
を使用する唯一の方法は、作業が到着したという通知を単にPushすることです。これにより、コンシューマーはRPOPLPUSH
をブロックすることができなくなります。Node.jsタグも追加します。これは、私が主に扱っている言語だからです。 Nodeは、シングルスレッドで非ブロッキングの性質を考えると、実装を簡単にする可能性がありますが、さらにnode-redisライブラリを使用しており、ソリューションは次のようにその長所と短所に敏感であるか敏感である必要があります上手。
Node.jsのメッセージキューにRedisを使用する必要があり、そのためのモジュールを使用してもかまわない場合は、 [〜#〜] rsmq [〜#〜] -Redisノードのシンプルなメッセージキュー。この質問が尋ねられた時点では利用できませんでしたが、今日は実行可能なオプションです。
あなたが質問で述べたように実際にキューを自分で実装したい場合は、RSMQのソースを読むことができますそれはまさにあなたが求めていることを行います。
見る:
これまでにいくつかの困難に遭遇しましたが、ここで文書化したいと思います。
これは難しい問題であり、メッセージキューの設計と実装では特に難しい問題です。メッセージは、コンシューマーがオフラインのときにどこかにキューイングできる必要があるため、単純なpub-subでは十分ではなく、コンシューマーはリスニング状態で再接続する必要があります。 ブロッキングポップは、べき等でないリスニング状態であるため、維持するのが難しい状態です。リスニングはべき等の操作である必要がありますが、ブロッキングポップに関して切断を処理する場合、切断が操作の成功直後に発生したのか、操作が失敗する直前に発生したのかを非常によく考えることができます。これは乗り越えられないわけではありませんが、望ましくありません。
さらに、リスニング操作はできるだけ単純にする必要があります。理想的には、次のプロパティが必要です。
私は今、Redis PUBSUB + RPOPLPUSHソリューションを支持しています。これにより、作業の通知が作業の消費から切り離され、クリーンなリスニングソリューションを除外できます。 PUBSUBは、作業の通知のみを担当します。 RPOPLPUSHのアトミックな性質は、消費、および作業を1人のコンシューマーに委任することを担当します。このソリューションは、最初はブロッキングポップに比べて不必要に複雑に見えましたが、今では複雑さはまったく不要ではありませんでした。それは難しい問題を解決していました。
ただし、このソリューションはそれほど簡単ではありません。
PUBSUB/RPOPLPUSHデザインにもスケーリングの問題があることに注意してください。Everyコンシューマは、everyメッセージ、これは不要なボトルネックがあることを意味します。チャネルを使用して作業を分割することは可能ではないかと思いますが、これはおそらくうまくいくにはトリッキーな設計です。
したがって、RedisよりもRabbitMQを使用することを選択する最大の理由は、障害シナリオとクラスタリングです。
この記事では実際にそれを最もよく説明しているので、リンクを提供します。
https://aphyr.com/posts/283-jepsen-redis
Redisセンチネルと最近のredisクラスタリングは、キューにとって悪い選択となった多くの非常に基本的な障害シナリオを処理できません。
RabbitMQには一連の問題がありますが、本番環境では非常に堅固で、優れたメッセージキューです。
これはうさぎの投稿です:
https://aphyr.com/posts/315-jepsen-rabbitmq
CAPの定理(一貫性、可用性、およびパーティション処理)を見ると、3つのうち2つしか選択できません。CP(一貫性とパーティション処理)のRMQをメッセージの負荷に活用していますが、利用できない場合は、世界の終わり。メッセージを失わないようにするために、メッセージを失わないようにするために、パーティション処理にignoreを使用します。ソースがUUIDを管理するため、重複を処理できます。