著者:ジユンHYY
出典:https://blog.csdn.net/LO_YUN/article/details/103949317
プロジェクトではMQを使用しているため、メッセージ損失の問題を考慮することは避けられません。金銭の取引が関係する一部のシナリオでは、メッセージの損失が致命的な場合があります。それでは、RocketMQでのメッセージ損失のシナリオは何ですか?
まず、最も簡単な消費フローチャートを見てみましょう。
上の図には、おおよそいくつかのシナリオが含まれています。
-
プロデューサーはメッセージを生成し、それをRocketMQに送信します
-
RocketMQがメッセージを受信したら、メッセージをディスクに保存する必要があります。そうしないと、停電やダウンタイムの後にデータが失われます。
-
コンシューマーはRocketMQからメッセージの消費を取得します。消費が成功すると、プロセス全体が終了します
次の図に示すように、これらの3つのシナリオはメッセージの損失を引き起こす可能性があります。
1.プロデューサーがシナリオ1でRocket MQにメッセージを送信すると、ネットワークジッタまたは異常な通信があると、メッセージが失われる可能性があります。
2.シナリオ2では、メッセージをディスクに永続化する必要があります。現時点では、メッセージが失われる状況が2つあります。
-
ディスクIOを削減するために、RocketMQはディスクに直接書き込むのではなく、最初にosキャッシュにメッセージを書き込みます。osキャッシュからメッセージを取得するコンシューマーは、メモリから直接メッセージを取得するのと同じで、より高速です。時間はosスレッドによって非同期にディスクにフラッシュされ、メッセージの永続化は完全に完了します。このプロセスでは、メッセージが非同期フラッシュディスクを完了していない場合、RocketMQのブローカーがダウンし、メッセージが失われます。
-
メッセージがディスクにフラッシュされたが、データはバックアップされていない場合、ディスクが損傷すると、メッセージも失われます。
3.コンシューマはRocketMQからメッセージを正常に取得します。メッセージが完全に消費されない場合、メッセージが消費されたことがRocketMQに通知され、コンシューマがダウンしますが、RocketMQはコンシューマがデータを正常に消費したと考えます。データはまだ失われています。
では、メッセージの損失をゼロにする方法は?
1.シナリオ1でメッセージが失われないようにするソリューションは、RocketMQ独自のトランザクションメカニズムを使用してメッセージを送信することです。一般的なプロセスは次のとおりです。
-
最初に、プロデューサーはハーフメッセージをRocketMQに送信します。このとき、コンシューマーはハーフメッセージを消費できません。ハーフメッセージの送信に失敗した場合、対応するロールバックロジックが実行されます。
-
半分のメッセージが正常に送信され、RocketMQが正常な応答を返した後、プロデューサのコアリンクが実行されます
-
プロデューサー自身のコアリンクが実行に失敗すると、ロールバックされ、RocketMQに半分のメッセージを削除するよう通知します
-
プロデューサーのコアリンクが正常に実行されると、RocketMQコミットハーフメッセージが通知され、コンシューマーがこのデータを使用できるようになります。
その中には、コミット/ロールバック操作のプロデューサーから長い間応答を受けていないRocketMQがあります。プロデューサーインターフェイスの詳細をコールバックしてください。興味がある場合は、「RocketMQ分散トランザクション原則」(https://blog.csdn.net)を参照してください。 / LO_YUN / article / details / 101673893)
RocketMQトランザクションを使用してプロデューサーのメッセージをRocketMQに正常に送信した後、この段階でメッセージが失われないことが保証されます。
2.シナリオ2でメッセージが失われないようにするには、まずosキャッシュの非同期フラッシュ戦略を同期フラッシュに変更する必要があります。この手順では、ブローカー構成ファイルを変更し、flushDiskTypeをSYNC_FLUSH同期フラッシュ戦略に変更する必要があります。デフォルトはASYNC_FLUSHは、ディスクを非同期にフラッシュします。
同期の点滅が正常に戻ったら、メッセージがディスクに永続化されていることを確認する必要があります。データを失うことなくディスクが損傷したことを確認するには、RocketMQ、クラスターデプロイメント、および複数のフォロワーのリーダーのデータにマスター/スレーブメカニズムを採用する必要があります。すべてに単一障害点を防ぐためのバックアップがあります。
3.シナリオ3では、メッセージがコンシューマーに到達すると、RocketMQはメッセージがコードで失われないようにすることができます
//注册消息监听器处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){
//对消息进行处理
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
上記のコードでは、RocketMQはリスナーをコンシューマーに登録し、コンシューマーがメッセージを取得すると、リスナー関数をコールバックして内部のメッセージを処理します。
メッセージが処理されると、メッセージはConsumeConcurrentlyStatus.CONSUME_SUCCESSに戻ります。CONSUME_SUCCESSが返された場合にのみ、コンシューマーはRocketMQに消費が完了したことを通知します。コンシューマーがダウンしている場合、メッセージは処理されており、メッセージは失われません。
CONSUME_SUCCESSに戻る前にコンシューマーがダウンしている場合、RocketMQはコンシューマーノードがダウンしていると見なし、自動的にフェイルオーバーして、メッセージをコンシューマーグループ内の他のコンシューマーに渡し、メッセージを消費してメッセージを保証します。失われていない
メッセージが失われないようにするためには、consumeMessageメソッドでメッセージ消費のビジネスロジックを直接書き込むだけで十分です。次のコードなどの操作を行う必要がある場合
//注册消息监听器处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){
//开启子线程异步处理消息
new Thread() {
public void run() {
//对消息进行处理
}
}.start();
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
新しく開いた子スレッドがメッセージを非同期で処理する場合、メッセージがまだ消費されていない可能性があり、コンシューマーはRocketMQにメッセージが消費されたことを通知し、その結果、ダウンタイムによりメッセージが失われます。
上記の一連のソリューションを使用すると、RocketMQを使用するときにメッセージの損失がゼロになることが保証されますが、パフォーマンスとスループットも大幅に低下します
-
トランザクションメカニズムを使用してメッセージを送信すると、通常のメッセージ送信よりも多くの手順が実行され、パフォーマンスが消費されます。
-
同期点滅は非同期点滅と比較され、1つはディスクに格納され、もう1つはメモリに格納されます。速度はまったく桁違いです。
-
マスタースレーブ組織の場合、リーダーはデータをフォロワーに同期させる必要があります
-
消費中に非同期に消費することはできません。消費が完了するのを待ってから、消費が完了したことをRocketMQに通知することができます
メッセージ損失ゼロは両刃の剣です。それを上手に使用したい場合でも、それは特定のビジネスシナリオに依存します。適切なソリューションを選択することが最善です