分散トピック{2} Rabbitが遅延キューをどのように実装しているかわからないため、ついに大きな工場に入ることができませんでした


rabbitmqを勉強したことのある学生は、rabbitmqには遅延キュー機能がないことを知っておく必要があります。インタビュアーがまだこの奇妙な質問をするのはなぜですか。
インタビュアーが知識を統合する論理的能力をテストするためにこの質問をするからです。

ここで確実に言えます。rabbitmqは遅延キューの機能を実装していませんが、デッドレターキュー+ TTLを使用すると、遅延キューの機能も実現できます。

遅延キュープラグインを介して実装する別の方法があります。これについては後で紹介します。

キューの使用シナリオの遅延

最もよく使われる場所は、注文をキャンセルするための時間外の注文支払いです。

それを実装する方法について話す前に、まずデッドレターキューとTTLとは何かを紹介しましょう。

キーポイントの説明

デッドレターキュー

rabbitmqでは、デッドレターキューは実際にはデッドレタースイッチと呼ばれる必要がありますが、このデッドレターはどういう意味ですか?
デッドレターキューと通常のキューに違いはありませんが、ユーザーがこのキューにメッセージを積極的に送信したり、切り替えたりすることはありません。次の状況が発生した場合にのみ、メッセージは元のキューからデッドレターキューに転送されます。

  • 元のキューのメッセージ長が所定の制限を超えています
  • コンシューマーはメッセージbasicNack / basicRejectを拒否し、メッセージをキューに戻しません。
  • 元のキューメッセージには有効期限があります。有効期限が切れる前にコンシューマーによって消費されていない場合は、デッドレターキューにも転送されます。

デッドレターキューの関連する設定パラメータは、キューに設定されます:x-dead-letter-exchange
x-dead-letter-routing-key
ここに画像の説明を挿入

TTL

TTLのフルネームはTimeTo Liveであり、これは有効期限に変換されます。メッセージがttlで設定された時間まで存続し、消費されていない場合、メッセージはクリアされます。Rabbitは、期限切れのメッセージをキューに設定するか、期限切れに設定できます。特定のメッセージに関するメッセージ。、ここに小さなインタビューの質問があります:

Q:有効期限が設定されたメッセージをrabbitはどのように処理しますか?

回答:Rabbitは、有効期限をクリーンアップするための遅延戦略を実装しています。目的は、メッセージキューの高スループットを確保することです。この遅延戦略は、メッセージがキューの先頭に到達した後、ブローカーがキューに有効期限が設定されている場合は、有効期限が到来したかどうかを確認します。到達した場合、メッセージは破棄され、メッセージはプッシュされません。応答しないでください。ブローカーは各メッセージをトラバースし、有効期限を確認してください。

すでに2つの重要な技術的ポイントを紹介しましたが、今度はこの記事のトピックに入るときです。rabbitmqはどのように遅延キューを実装しますか?

TTL + DLXを使用する

実現アイデア

おそらく、TTLとデッドレターキューについての私の説明の後、あなたはそれを実装する方法をすでに知っているかもしれません、しかしそれを実装する方法を知っていても、私はまだそれについて話さなければなりません、ハハ

TTLはメッセージの有効期限を設定でき、デッドレターキューに入る条件の1つがあるため、元のキューメッセージには有効期限が設定されています。有効期限が切れる前に消費者によって消費されていない場合は、有効期限も設定されます。キュー内のデッドレターに転送され、 2つを組み合わせてこれを行うことができます。通常のビジネスを処理するリスナーはデッドレターキューをリッスンし、デッドレターキューのパラメータを通常のキューに設定してから、メッセージフローは次のようになります。

  • 有効期限が10000ミリ秒のメッセージをブローカーに送信しました
  • ブローカーはメッセージをキューに入れます
  • 10000ミリ秒後、メッセージは消費されていません
  • ブローカーはメッセージをデッドレターエクスチェンジに転送し、デッドレターエクスチェンジはメッセージをデッドレターキューにプッシュします
  • デッドレターキューをリッスンするリスナーを設定したばかりなので、10000ミリ秒後にこのメッセージを受信したはずです。

コードの記述

  • プロデューサーキューと交換バインディングおよびキュー宣言
@Configuration
public class RabbitMQConfig {
    
    
    public static final String QUEUE_TEST_DLX = "queue_test_dlx";
    public static final String QUEUE_TEST_NORMAL = "queue_test_normal";
    public static final String EXCHANGE_TEST_DLX = "exchange_test_dlx";
//    声明一个默认不进行消费的队列 绑定死信队列交换机和死信队列的key
    @Bean("queueTestNormal")
    public Queue queueTestNormal() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_NORMAL).deadLetterExchange(EXCHANGE_TEST_DLX).deadLetterRoutingKey("testdlx").build();
    }
    //    声明死信队列
    @Bean("queueTestDLX")
    public Queue queueTestDLX() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_DLX).build();
    }
    //  声明死信交换机
    @Bean("exchangeTestDLX")
    public Exchange exchangeTestDLX() {
    
    
        return ExchangeBuilder.directExchange(EXCHANGE_TEST_DLX).durable(true).build();
    }
    //    死信队列与死信交换机绑定
    @Bean
    public Binding itemQueueExchange7(@Qualifier("queueTestDLX") Queue queue,
                                      @Qualifier("exchangeTestDLX") Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("testdlx").noargs();
    }
    }
  • プロデューサーは単にメッセージを通常のキューに送信し、有効期限を10秒に設定します
    @Test
    public void testDLX() {
    
    
        rabbitTemplate.convertAndSend(null, "queue_test_normal","我是10秒之后才到的", new MessagePostProcessor() {
    
    
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
    
    
                MessageProperties messageProperties = message.getMessageProperties();
                messageProperties.setExpiration(10000+"");
                return message;
            }
        });
        System.out.println("我发送消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("开始倒计时:10");
        int i = 10;
        while (true){
    
    
            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            if(i>0){
    
    
                System.out.println("倒计时:"+(--i));
            }

        }
    }
  • 消費者リスナーの執筆
    @RabbitListener(queues = "queue_test_dlx")
    public void onMessage5(Message message, Channel channel) throws Exception {
    
    
        System.out.println("我收到消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("收到消息来自队列queue_test_dlx:" + new String(message.getBody()));
    }

総括する

これまでのところ。遅延キューが実装されました。ここで、遅延キューを実現するためのこの方法の唯一の欠点を要約します。

時間に間に合わない:メッセージがキューの先頭に到達した場合にのみ、ブローカーはメッセージの有効期限が切れているかどうかを確認してプッシュし、有効期限が設定されたメッセージの前に有効期限の長いメッセージを追加します。有効期限が短くなりますメッセージは処理されておらず、キューで待機しています。

このため、rabbitmqは遅延キュープラグインを導入しました。このプラグインの実装の考え方は以前の実装とは異なります。メッセージに遅延時間が設定されている場合、メッセージをすぐにキューにプッシュすることはありません。 、ただしメッセージを待ちます。設定された遅延時間の後にキューに入れられます。ここで、遅延キュープラグインの実装方法を紹介します。

遅延キュープラグインを使用する

遅延キュープラグインをインストールする

#下载插件 https://www.cnblogs.com/geekdc/p/13549613.html

docker cp /Users/yangle/docker/rabbitmq/plugins/rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez rabbitmq:/plugins

#进入容器

docker exec -it rabbitmq /bin/bash

#启用插件

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

#查看

rabbitmq-plugins list

#重新启动容器

docker restart rabbitmq

コードの記述

  • バインディング構成ファイルの切り替えとキュー
@Configuration
public class RabbitMQConfig {
    
    
    public static final String QUEUE_TEST_DELAY_PLUGIN = "queue_test_delay_plugin";
    public static final String EXCHANGE_TEST_DELAY_PLUGIN = "exchange_test_delay_plugin";
     //    声明一个队列
    @Bean("queueDelayPlugin")
    public Queue queueDelayPlugin() {
    
    
        return QueueBuilder.durable(QUEUE_TEST_DELAY_PLUGIN).build();
    }
    @Bean
    CustomExchange delayExchange(){
    
    
        Map<String, Object> args = new HashMap<>();
        // 设置为路由模式
        args.put("x-delayed-type", "direct");
        // type必须设置为x-delayed-message
        return new CustomExchange(EXCHANGE_TEST_DELAY_PLUGIN, "x-delayed-message", true,false, args);
    }

    //    插件交换机与队列绑定
    @Bean
    public Binding itemQueueExchange8(@Qualifier("queueDelayPlugin") Queue queue,
                                      @Qualifier("delayExchange") Exchange exchange) {
    
    
        return BindingBuilder.bind(queue).to(exchange).with("testDelayPlugin").noargs();
    }}
  • メッセージを送る
    @Test
    public void testDelayPlugin() {
    
    
        rabbitTemplate.convertAndSend("exchange_test_delay_plugin", "testDelayPlugin", "测试延时插件发送消息", new MessagePostProcessor() {
    
    
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
    
    
                message.getMessageProperties().setDelay(10000);
                return message;
            }
        });
    }
  • リスナー
    @RabbitListener(queues = "queue_test_delay_plugin")
    public void onMessage6(Message message, Channel channel) throws Exception {
    
    
        System.out.println("我收到消息的时间为:"+(System.currentTimeMillis()));
        System.out.println("收到消息来自队列queue_test_delay_plugin:" + new String(message.getBody()));
    }

総括する

プラグインは遅延キューを実装するのは簡単ですが、次のような制限もあります。

  • パフォーマンスが低下するため、そのような要求がない場合は使用しないでください。
  • このプラグインは、100万や1億などの大量のデータを含む遅延メッセージには適していません。
  • 遅延時間:0 <= n <=(2 ^ 32)-1、ミリ秒単位。

Wechatで検索を検索[LeZaiオープントーク]ハンサムな私をフォローし、[乾物を受け取る]と返信すると、Javaの基本、Javaの同時実行など、選択を待っているインタビュー資料や建築家の必読の本がたくさんあります。マイクロサービス、ミドルウェアなど。より多くの情報があなたを待っています。

おすすめ

転載: blog.csdn.net/weixin_34311210/article/details/109701943