RabbitMQ の信頼性: メッセージ配信中の信頼性の保証

著者: 禅とコンピュータープログラミングの芸術

1 はじめに

RabbitMQ (Rabbit Message Queue) は、AMQP プロトコルに基づくオープンソースの多目的メッセージ キューです。これはもともと金融システムに由来し、分散システムでデータを保存、転送、交換するために使用されます。しかし、時間の経過とともに、ますます多くのアプリケーションや分野で使用されるようになりました。現在、多くの企業が RabbitMQ を使用して内部メッセージ通信やサービス通信を実装しています。したがって、RabbitMQ を理解することがますます重要になります。この記事では、著者自身の実務経験を組み合わせて、RabbitMQ のメッセージ信頼性メカニズムについて詳しく説明します。

2. 基本的な概念と用語の説明

2.1 メッセージモデル

まず、RabbitMQ のメッセージ モデルを簡単に理解する必要があります。RabbitMQ には、プロデューサー、エクスチェンジ、キュー、コンシューマーの 3 種類のエンティティがあります。

プロデューサー

プロデューサは、RabbitMQ にメッセージを送信するエンティティです。メッセージは、アプリケーションから API を呼び出してキューまたはエクスチェンジに投稿できます。プロデューサーはさまざまなオプションを選択して、メッセージをいつどのキューまたはエクスチェンジに配信するかを決定できます。また、メッセージの優先順位、タイムアウト、繰り返し回数などのメッセージ属性を指定することもできます。プロデューサは複数のリターン アドレスを設定することもでき、何らかの理由でメッセージをキューまたはエクスチェンジに配信できない場合、メッセージは指定されたリターン アドレスにルーティングされて再試行されます。

スイッチ

RabbitMQ のスイッチは、現実世界のスイッチに似ています。受信した情報を保存し、その情報をいくつかのルールに従って対応するキューに渡す責任があります。各キューは、スイッチ上のルーティング キーにバインドされるように構成できます。プロデューサがスイッチにメッセージをパブリッシュすると、スイッチはメッセージのルーティング キーとバインドされたルーティング キーに基づいて、メッセージを対応するキューに配信します。

キューは、メッセージを保存するために使用される一時的なコンテナです。RabbitMQ は多くのキューを作成でき、それぞれのキューにさまざまな種類のメッセージを保存できます。たとえば、オンデマンド キュー、ライブ ブロードキャスト キュー、プライベート メッセージ キューの 3 つの異なるタイプのキューを作成できます。もちろん、混合タイプのキューを作成することもできます。たとえば、オンデマンド メッセージとライブ ブロードキャスト メッセージの両方を同じキューに保存します。ここでは、メッセージの信頼性に関する内容のみを説明するため、ここでのキューの種類は、単純にオンデマンド キュー、ライブ ブロードキャスト キュー、またはプライベート メッセージ キューとして理解できます。

消費者

コンシューマは、RabbitMQ からメッセージを取得して処理するエンティティです。キューにメッセージがある場合、コンシューマーはこれらのメッセージを取得して処理できます。コンシューマーはプリフェッチ カウント属性を設定して、一度に取得するメッセージの数を示すことができ、これによりパフォーマンスが向上します。同時に、QoS パラメータ、つまりサービス品質を設定して、RabbitMQ によるメッセージ処理の速度を制御することもできます。QoS は、プリフェッチ数とプリフェッチ サイズの 2 つのパラメータで設定できます。プリフェッチ数は同時に取得できるメッセージの最大数を示し、プリフェッチ サイズは取得される各メッセージのサイズ制限を示します。

2.2 信頼性

メッセージの信頼性は非常に重要な問題であり、RabbitMQ によって提供される重要な機能の 1 つです。信頼できるメッセージ配信メカニズムがないため、メッセージがターゲットのキューまたは交換に正しく配信されたとしても、そのメッセージがコンシューマーによって受信されるという保証はありません。したがって、RabbitMQ は、メッセージ送信の信頼性を確保するために、さまざまな信頼性メカニズムを提供します。以下に、RabbitMQ の主な信頼性メカニズムを紹介します。

2.2.1 トランザクション

RabbitMQ はトランザクションをサポートします。つまり、一連のアクションが実行されるか実行されないかのいずれかです。トランザクションが正常に完了すると、すべてのアクションが実行されますが、トランザクションが失敗した場合は、アクションは実行されません。トランザクションを使用すると、メッセージ配信中にすべてが完了するか、何も完了しないことが保証されます。

//开启事务
channel.txSelect();
try{
    // 发送消息
    channel.basicPublish("","",null,message);
    //提交事务
    boolean commitSuccess = channel.txCommit();
    if(commitSuccess){
        System.out.println("事务提交成功!");
    }else {
        System.out.println("事务提交失败!");
    }
}catch (Exception e){
    try {
        //回滚事务
        boolean rollbackSuccess = channel.txRollback();
        if(rollbackSuccess){
            System.out.println("事务回滚成功!");
        }else {
            System.out.println("事务回滚失败!");
        }
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}finally {
    //关闭连接
    channel.close();
    connection.close();
}

2.2.2 発行者の確認

パブリッシャー確認は、RabbitMQ の信頼性メカニズムです。プロデューサが送信したメッセージを確認した後、コンシューマがメッセージを受信したことを確認できます。これにより、メッセージが失われることがなくなり、必要に応じて再試行できるようになります。

boolean isConfirm = true;
if(isConfirm){
    channel.confirmSelect();
}
ListenableFuture<Sent> future = channel.basicPublish("", "", null, message);
if(!isConfirm){
    future.addCallback(new FutureCallback<Sent>() {
        @Override
        public void onFailure(Throwable throwable) {
            // 抛出异常时重试
            doRetry(future);
        }

        @Override
        public void onSuccess(Sent sent) {
            // 确认消息投递成功
        }
    });
}else {
    future.addCallback(new PublishConfirmListener());
}
...
class PublishConfirmListener implements ConfirmListener {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        // 当消息投递到队列并得到ACK时,调用此方法
    }

    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        // 当消息投递到队列但没得到ACK时,调用此方法
        doRetry(deliveryTag);
    }

    private void doRetry(Object arg) {
        // 重新发送已确认但未确认的消息
       ...
    }

    private void doRetry(long deliveryTag) {
        // 根据deliveryTag重新发送已确认但未确认的消息
       ...
    }
}

2.2.3 持久化

RabbitMQ はメッセージの永続化をサポートします。これは、メッセージがキュー内で一時停止され、メモリ内に保持されるだけでなく、ディスクに保存されることを意味します。コンシューマが突然クラッシュした場合でも、以前に公開されたメッセージはディスクに保存されているため、影響を受けません。永続メッセージにより、メッセージの信頼性の高い配信が保証されます。

2.2.4 メッセージ永続性の追跡

メッセージが永続化されると、RabbitMQ は、メッセージが消費されることが確認されたかどうか、メッセージが永続化されるかどうかなど、メッセージのメタデータを記録します。これは、メッセージのステータスを追跡し、いつ安全に削除できるかを知るのに役立ちます。

2.2.5 デッドレターキュー

RabbitMQ は、さまざまな理由 (キューの長さが長すぎる、コンシューマの処理エラーなど) により特定のメッセージをドロップした場合、これらのメッセージをデッド レター キューに再ルーティングすることでメッセージを節約できます。メッセージの TTL (Time To Live) 期限切れやメッセージの拒否 (再配信) など、メッセージをデッドレター キューに再ルーティングするかどうかを決定するために、特定の条件を設定できます。

2.2.6 フロー制御

フロー制御とは、コンシューマーのメッセージ処理能力に基づいて、メッセージ配信プロセス中のメッセージの数を制限することを指します。RabbitMQ は、プリフェッチ数を通じて 1 秒あたりに受信できるメッセージの数を設定することで、コンシューマーのメッセージ処理速度を制御します。

2.2.7 コピーキュー

メッセージの信頼性の高い配信を保証するために、RabbitMQ はレプリカ キューと呼ばれる複数のキューの作成をサポートしています。これらのキューは同じメッセージを保持できますが、実際のキューとして選択されるのはレプリカ キューの 1 つだけです。フェイルオーバーが発生すると、メッセージが間違ったキューから新しいキューに自動的にコピーされる可能性があります。このアプローチにより、メッセージ損失の可能性が軽減されます。

3. コアアルゴリズム原理、具体的な操作手順、数式の説明

3.1 マルチレベルのフロー制御

RabbitMQ は、メッセージの流入速度を制限するために、マルチレベルのフロー制御戦略を使用して設計されています。より良いパフォーマンスを実現するために、RabbitMQ はデフォルトで優先キューを使用します。プライオリティ キューはメッセージの順序を保証しますが、RabbitMQ 設定項目ではキューのコンシューマーの最大数を制御でき、これにより各キューの消費率が制限されます。キューあたりのコンシューマの最大数が k であると仮定すると、理想的には、i 番目の優先キューの消費率は qi = kp になるはずです。ここで、p はすべての優先キューのコンシューマの合計数です。RabbitMQ は、各キューの現在の最大消費率を計算することにより、キューのトラフィックをさらに調整します。次の図は、RabbitMQ のフロー制御戦略を示しています。

デフォルト設定では、RabbitMQ は各優先キューに仮想ノードを割り当てます。各仮想ノードは 1 つのコンシューマによってのみ使用できるため、各優先キュー間のトラフィックのバランスをとることができます。各仮想ノードについて、RabbitMQ は、現在の仮想ノードが受け取るべき重みを決定するために、以前のすべての仮想ノードの平均消費遅延をカウントします。この重みは、仮想ノードのメッセージ処理速度に直接影響します。重みは 1、2、3 の 3 つのレベルに分かれています。重みが大きいほど、仮想ノードのメッセージ処理速度が速くなります。各優先キューには k 個の仮想ノードがあり、各優先キューの仮想ノードは同じ重みを共有します。コンシューマが RabbitMQ に接続すると、RabbitMQ は初期の重みをコンシューマに割り当て (デフォルトは 1)、メッセージ処理の遅延履歴に基づいて重みを動的に調整します。

たとえば、キュ​​ーに 10 の仮想ノードがあり、1 秒あたり 50 のメッセージが消費されると仮定します。仮想ノードの平均遅延が 0.5 秒の場合、その重みは 5 です。コンシューマーの平均遅延が 1 秒の場合、その重みは 1 です。特定の優先キューの最大コンシューマー数が 100 の場合、キューの消費速度は約 100 × (k/p=5)/0.5 ≈ 100 × 5 / 2 ≈ 2000 msg/s となります。

コンシューマの処理能力が低下したり、現在のキューにメッセージのバックログが多すぎる場合、他のコンシューマが残りのメッセージを処理するまでブロックされます。

さらに、RabbitMQ は、メッセージのバックログを管理するための 2 つのメカニズムも提供します。1 つはパブリッシュ レート制限 (メッセージ パブリッシュ レート制限)、もう 1 つはバックプレッシャーです。

パブリッシュ レート制限は、パブリッシャーが 1 秒あたりにパブリッシュできるメッセージの数を指します。これは、パブリッシャー設定ファイルで設定できます。たとえば、「x-max-rate」: 100 は、パブリッシュを 1 秒あたり 100 メッセージに制限します。バックプレッシャーとは、RabbitMQ がコンシューマに短期間で過負荷がかかることを望まないため、新しいメッセージの受け入れを停止することを意味します。RabbitMQ は、メッセージ バックログが特定の値を超えると、メッセージのプッシュを停止します。

3.2 永続メッセージ要件を満たす

RabbitMQ はメッセージングに AMQP プロトコルを使用します。TCP/IP をトランスポート層プロトコルとして使用し、Erlang プログラミング言語を使用してサーバー側ロジックを実装します。Erlang は、仮想マシンで実行される解釈型関数型プログラミング言語です。

メッセージの永続性を確保するために、RabbitMQ はメッセージをディスクに保存し、複数のディスクに同期します。メッセージ ログを使用してメッセージを保存します。各メッセージには一意のメッセージ番号があり、メッセージ ログは順番に保存されます。RabbitMQ が起動すると、ディスク上のメッセージ ログを読み取り、メッセージ番号に従って並べ替えます。次に、RabbitMQ はメッセージ ログから対応するメッセージを読み取り、メッセージ キューにプッシュします。コンシューマがメッセージを消費すると、RabbitMQ はメッセージに削除のマークを付けます。新しいコンシューマがキューに参加した場合でも、RabbitMQ はメッセージ ログ内のメッセージを読み取り、メッセージ番号の順にキューにプッシュできます。

コンシューマーが特定のメッセージの消費を終了した後でも、コンシューマーがダウンすると、メッセージはメッセージ ログに残ります。RabbitMQ は、すべてのコンシューマーがメッセージを消費するまで、メッセージを削除されていないものとしてマークするだけです。その後、RabbitMQ はメッセージと対応するファイルをディスクから削除します。メッセージ バックログが大きい場合、ディスク使用量が大きくなる可能性があります。RabbitMQ は、メッセージ ログを圧縮してディスク領域の使用量を削減するプラグインを提供します。

おすすめ

転載: blog.csdn.net/universsky2015/article/details/133502538