RabbitMQ系列3 RabbitMQ工作模式介紹

RabbitMQ工作模式介紹

1. simple簡單模式

模式介紹

在這里插入圖片描述
  1. 消息產生著將消息放入隊列
  2. 消息的消費者(consumer) 監聽(while) 消息隊列,如果隊列中有消息,就消費掉,消息被拿走后,自動從隊列中刪除(隱患 消息可能沒有被消費者正確處理,已經從隊列中消失了,造成消息的丟失)應用場景:聊天(中間有一個過度的服務器;p端,c端)

代碼演示

生產者端

public class Producer {

    static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws Exception {

        //創建連接
        Connection connection = ConnectionUtil.getConnection();

        // 創建頻道
        Channel channel = connection.createChannel();

        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        for (int i = 1; i <= 30; i++) {
            // 發送信息
            String message = "你好;小兔子!work模式--" + i;
            /**
             * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
             * 參數2:路由key,簡單模式可以傳遞隊列名稱
             * 參數3:消息其它屬性
             * 參數4:消息內容
             */
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println("已發送消息:" + message);
        }

        // 關閉資源
        channel.close();
        connection.close();
    }
}

消費者端
**

package com.itheima.rabbitmq.work;

import com.itheima.rabbitmq.util.ConnectionUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer1 {

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();

        // 創建頻道
        Channel channel = connection.createChannel();

        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //一次只能接收并處理一個消息
        channel.basicQos(1);

        //創建消費者;并設置消息處理
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //路由key
                    System.out.println("路由key為:" + envelope.getRoutingKey());
                    //交換機
                    System.out.println("交換機為:" + envelope.getExchange());
                    //消息id
                    System.out.println("消息id為:" + envelope.getDeliveryTag());
                    //收到的消息
                    System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
                    Thread.sleep(1000);

                    //確認消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(Producer.QUEUE_NAME, false, consumer);
    }
}

2. work工作模式(資源的競爭)

模式介紹

在這里插入圖片描述
  1. 消息產生者將消息放入隊列消費者可以有多個,消費者1,消費者2,同時監聽同一個隊列,消息被消費?C1 C2共同爭搶當前的消息隊列內容,誰先拿到誰負責消費消息(隱患,高并發情況下,默認會產生某一個消息被多個消費者共同使用,可以設置一個開關(syncronize,與同步鎖的性能不一樣) 保證一條消息只能被一個消費者使用)
  2. 應用場景:紅包;大項目中的資源調度(任務分配系統不需知道哪一個任務執行系統在空閑,直接將任務扔到消息隊列中,空閑的系統自動爭搶)

代碼實現

就是比簡單模式多了一個消費者

生產者端

public class Producer {

    static final String QUEUE_NAME = "work_queue";

    public static void main(String[] args) throws Exception {

        //創建連接
        Connection connection = ConnectionUtil.getConnection();

        // 創建頻道
        Channel channel = connection.createChannel();

        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        for (int i = 1; i <= 30; i++) {
            // 發送信息
            String message = "你好;小兔子!work模式--" + i;
            /**
             * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
             * 參數2:路由key,簡單模式可以傳遞隊列名稱
             * 參數3:消息其它屬性
             * 參數4:消息內容
             */
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println("已發送消息:" + message);
        }

        // 關閉資源
        channel.close();
        connection.close();
    }
}

消費者1端
**

package com.itheima.rabbitmq.work;

import com.itheima.rabbitmq.util.ConnectionUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer1 {

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();

        // 創建頻道
        Channel channel = connection.createChannel();

        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //一次只能接收并處理一個消息
        channel.basicQos(1);

        //創建消費者;并設置消息處理
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //路由key
                    System.out.println("路由key為:" + envelope.getRoutingKey());
                    //交換機
                    System.out.println("交換機為:" + envelope.getExchange());
                    //消息id
                    System.out.println("消息id為:" + envelope.getDeliveryTag());
                    //收到的消息
                    System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
                    Thread.sleep(1000);

                    //確認消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(Producer.QUEUE_NAME, false, consumer);
    }
}

消費者2端
**

package com.itheima.rabbitmq.work;

import com.itheima.rabbitmq.util.ConnectionUtil;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Consumer1 {

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();

        // 創建頻道
        Channel channel = connection.createChannel();

        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(Producer.QUEUE_NAME, true, false, false, null);

        //一次只能接收并處理一個消息
        channel.basicQos(1);

        //創建消費者;并設置消息處理
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //路由key
                    System.out.println("路由key為:" + envelope.getRoutingKey());
                    //交換機
                    System.out.println("交換機為:" + envelope.getExchange());
                    //消息id
                    System.out.println("消息id為:" + envelope.getDeliveryTag());
                    //收到的消息
                    System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
                    Thread.sleep(1000);

                    //確認消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(Producer.QUEUE_NAME, false, consumer);
    }
}

結果
Consumer_queue2控制臺輸出

在這里插入圖片描述

Consumer_queue1控制臺輸出
**

在這里插入圖片描述

可以看到兩個消費者都去隊列中拿消息了
**

小結

如果一個隊列中有多個消費者,那么消費者之間對于同一消息的關系是競爭關系

Work Queues對于任務過重或任務較多情況使用工作隊列可以提高任務處理的速度,例如短信服務部署多個只要一個發送成功即可

3. publish/subscribe發布訂閱(共享資源)

模式介紹

在這里插入圖片描述

前面2個案例中,只有3個角色:
**

  • P:生產者,也就是要發送消息的程序

  • C:消費者:消息的接受者,會一直等待消息到來。

  • queue:消息隊列,圖中紅色部分

而在訂閱模型中,多了一個exchange角色,而且過程略有變化:

  • P:生產者,也就是要發送消息的程序,但是不再發送到隊列中,而是發給X(交換機)

  • C:消費者,消息的接受者,會一直等待消息到來。

  • Queue:消息隊列,接收消息、緩存消息。

  • Exchange:交換機,圖中的X。一方面,接收生產者發送的消息。另一方面,知道如何處理消息,例如遞交給某個特別隊列、遞交給所有隊列、或是將消息丟棄。到底如何操作,取決于Exchange的類型。Exchange有常見以下3種類型:

    • Fanout:廣播,將消息交給所有綁定到交換機的隊列

    • Direct:定向,把消息交給符合指定routing key 的隊列

    • Topic:通配符,把消息交給符合routing pattern(路由模式) 的隊列

Exchange(交換機)只負責轉發消息,不具備存儲消息的能力,因此如果沒有任何隊列與Exchange綁定,或者沒有符合路由規則的隊列,那么消息會丟失!

應用場景

消息產生者將消息放入交換機,交換機發布訂閱把消息發送到所有消息隊列中,對應消息隊列的消費者拿到消息進行消費

相關場景:郵件群發,群聊天,廣播(廣告)

發布訂閱模式:

1、每個消費者監聽自己的隊列。

2、生產者將消息發給broker,由交換機將消息轉發到綁定此交換機的每個隊列,每個綁定交換機的隊列都將接收到消息

代碼實現

生產者

package com.pjh;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Switch {
    //交換機名稱
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    //隊列名稱
    static final String FANOUT_QUEUE_1 = "fanout_queue_1";
    //隊列名稱
    static final String FANOUT_QUEUE_2 = "fanout_queue_2";

    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*創建頻道*/
        Channel channel = connection.createChannel();
        /**
         * 聲明交換機
         * 參數1:交換機名稱
         * 參數2:交換機類型,fanout、topic、direct、headers
         */
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);
        /*聲明創建隊列*/
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
        channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind(FANOUT_QUEUE_1, FANOUT_EXCHAGE, "");
        channel.queueBind(FANOUT_QUEUE_2, FANOUT_EXCHAGE, "");
        for (int i = 1; i <= 10; i++) {
            // 發送信息
            String message = "第" + i+"條消息";
            /**
             * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
             * 參數2:路由key,簡單模式可以傳遞隊列名稱
             * 參數3:消息其它屬性
             * 參數4:消息內容
             */
            channel.basicPublish(FANOUT_EXCHAGE, "", null, message.getBytes());
            System.out.println("已發送消息:" + message);
        }
        /*關閉資源*/
        channel.close();
        connection.close();
    }
}

消費者1

package com.pjh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Switch_Consumer {
    //交換機名稱
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    //隊列名稱
    static final String FANOUT_QUEUE_1 = "fanout_queue_1";
    //隊列名稱
    static final String FANOUT_QUEUE_2 = "fanout_queue_2";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*獲取channel*/
        Channel channel = connection.createChannel();
        /*聲明交換機*/
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);
        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind(FANOUT_QUEUE_1, FANOUT_EXCHAGE,"");
        /*創建消費者并設置消息處理*/
       Consumer consumer = new DefaultConsumer(channel) {
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key為:" + envelope.getRoutingKey());
                //交換機
                System.out.println("交換機為:" + envelope.getExchange());
                //消息id
                System.out.println("消息id為:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(FANOUT_QUEUE_1,true,consumer);
    }
}

消費者2

package com.pjh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Switch_Consumer2 {
    //交換機名稱
    static final String FANOUT_EXCHAGE = "fanout_exchange";
    //隊列名稱
    static final String FANOUT_QUEUE_1 = "fanout_queue_1";
    //隊列名稱
    static final String FANOUT_QUEUE_2 = "fanout_queue_2";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*獲取channel*/
        Channel channel = connection.createChannel();
        /*聲明交換機*/
        channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT);
        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind(FANOUT_QUEUE_2, FANOUT_EXCHAGE,"");
        /*創建消費者并設置消息處理*/
       Consumer consumer = new DefaultConsumer(channel) {
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key為:" + envelope.getRoutingKey());
                //交換機
                System.out.println("交換機為:" + envelope.getExchange());
                //消息id
                System.out.println("消息id為:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消費者2-接收到的消息為:" + new String(body, "utf-8"));
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(FANOUT_QUEUE_2,true,consumer);
    }
}

查看管控臺可以發現多了一個交換機

在這里插入圖片描述

測試

啟動所有消費者,然后使用生產者發送消息;在每個消費者對應的控制臺可以查看到生產者發送的所有消息;到達廣播的效果。

消費者1控制臺輸出

在這里插入圖片描述

消費者2控制臺輸出

在這里插入圖片描述

在執行完測試代碼后,其實到RabbitMQ的管理后臺找到Exchanges選項卡,點擊 fanout_exchange 的交換機,可以查看到如下的綁定:

在這里插入圖片描述

小結

交換機需要與隊列進行綁定,綁定之后:一個消息可以被多個消費者都收到

發布訂閱模式與工作隊列模式的區別:

** 1.工作隊列模式不用定義交換機,而發布/訂閱模式需要訂閱交換機
** 2.
發布/訂閱模式生產方向是面向交換機發送消息,工作隊列模式的生產是面向隊列發送消息(底層使用默認交換機)
3.發布/訂閱模式需要設置隊列和交換機的綁定,工作隊列模式不需要設置,實際上工作隊列模式會將隊列綁定到默認的交換機

4.Routing路由模式

模式介紹

路由模式特點:

  • 隊列與交換機的綁定,不能是任意綁定了,而是要指定一個RoutingKey(路由key)

  • 消息的發送方在 向 Exchange發送消息時,也必須指定消息的 RoutingKey。

  • Exchange不再把消息交給每一個綁定的隊列,而是根據消息的Routing Key進行判斷,只有隊列的Routingkey與消息的 Routing key完全一致,才會接收到消息

在這里插入圖片描述
  • P:生產者,向Exchange發送消息,發送消息時,會指定一個routing key。

  • X:Exchange(交換機),接收生產者的消息,然后把消息遞交給 與routing key完全匹配的隊列

  • C1:消費者,其所在隊列指定了需要routing key 為 error 的消息

  • C2:消費者,其所在隊列指定了需要routing key 為 info、error、warning 的消息

代碼實現

在編碼上與 Publish/Subscribe發布與訂閱模式 的區別是交換機的類型為:Direct,還有隊列綁定交換機的時候需要指定routing key。

生產者

package com.pjh;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Routing {
    //交換機名稱
    static final String DIRECT_EXCHAGE = "direct_exchange";
    //隊列名稱
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    //隊列名稱
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*創建頻道*/
        Channel channel = connection.createChannel();
        /**
         * 聲明交換機
         * 參數1:交換機名稱
         * 參數2:交換機類型,fanout、topic、direct、headers
         */
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);
        /*聲明創建隊列*/
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);
        channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind(DIRECT_QUEUE_INSERT,DIRECT_EXCHAGE, "insert");
        channel.queueBind(DIRECT_QUEUE_UPDATE, DIRECT_EXCHAGE, "update");
        // 發送信息
        String message = "新增了商品。路由模式;routing key 為 insert " ;
        /**
         * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
         * 參數2:路由key,簡單模式可以傳遞隊列名稱
         * 參數3:消息其它屬性
         * 參數4:消息內容
         */
        channel.basicPublish(DIRECT_EXCHAGE, "insert", null, message.getBytes());
        System.out.println("已發送消息:" + message);

        // 發送信息
        message = "修改了商品。路由模式;routing key 為 update" ;
        /**
         * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
         * 參數2:路由key,簡單模式可以傳遞隊列名稱
         * 參數3:消息其它屬性
         * 參數4:消息內容
         */
        channel.basicPublish(DIRECT_EXCHAGE, "update", null, message.getBytes());
        System.out.println("已發送消息:" + message);
        /*關閉資源*/
        channel.close();
        connection.close();
    }
}

消費者1

package com.pjh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Routing_Consumer {
    //交換機名稱
    static final String DIRECT_EXCHAGE = "direct_exchange";
    //隊列名稱
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    //隊列名稱
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*獲取channel*/
        Channel channel = connection.createChannel();
        /*聲明交換機*/
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);
        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind( DIRECT_QUEUE_INSERT,DIRECT_EXCHAGE,"insert");
        /*創建消費者并設置消息處理*/
       Consumer consumer = new DefaultConsumer(channel) {
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key為:" + envelope.getRoutingKey());
                //交換機
                System.out.println("交換機為:" + envelope.getExchange());
                //消息id
                System.out.println("消息id為:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(DIRECT_QUEUE_INSERT,true,consumer);
    }
}

消費者二

package com.pjh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Routing_Consumer2 {
    //交換機名稱
    static final String DIRECT_EXCHAGE = "direct_exchange";
    //隊列名稱
    static final String DIRECT_QUEUE_INSERT = "direct_queue_insert";
    //隊列名稱
    static final String DIRECT_QUEUE_UPDATE = "direct_queue_update";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*獲取channel*/
        Channel channel = connection.createChannel();
        /*聲明交換機*/
        channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT);
        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind( DIRECT_QUEUE_UPDATE,DIRECT_EXCHAGE,"update");
        /*創建消費者并設置消息處理*/
       Consumer consumer = new DefaultConsumer(channel) {
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key為:" + envelope.getRoutingKey());
                //交換機
                System.out.println("交換機為:" + envelope.getExchange());
                //消息id
                System.out.println("消息id為:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消費者1-接收到的消息為:" + new String(body, "utf-8"));
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(DIRECT_QUEUE_UPDATE,true,consumer);
    }
}

測試

消費者1客戶端輸出

在這里插入圖片描述

消費者2客戶端輸出

在這里插入圖片描述

在執行完測試代碼后,其實到RabbitMQ的管理后臺找到Exchanges選項卡,點擊 direct_exchange 的交換機,可以查看到如下的綁定:

在這里插入圖片描述

小結

Routing模式要求隊列在綁定交換機時要指定routing key,消息會轉發到符合routing key的隊列。

5.Topics通配符模式

模式說明

Topic類型與Direct相比,都是可以根據RoutingKey把消息路由到不同的隊列。只不過Topic類型Exchange可以讓隊列在綁定Routing key 的時候使用通配符!
Routingkey 一般都是有一個或多個單詞組成,多個單詞之間以”.”分割,例如: item.insert
通配符規則:
#:匹配一個或多個詞
*:匹配不多不少恰好1個詞
舉例:
item.#:能夠匹配item.insert.abc 或者 item.insert
item.*:只能匹配item.insert

在這里插入圖片描述
  • 紅色Queue:綁定的是usa.# ,因此凡是以 usa.開頭的routing key 都會被匹配到

  • 黃色Queue:綁定的是#.news ,因此凡是以 .news結尾的 routing key 都會被匹配

代碼實現

案例:
在生產者端插入倆個以“pjh.insert”,“pjh.update”為路由key的消息
在消費者端使用“pjh.*”為通配符接受消息

生產者

package com.pjh;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Topic {
    //交換機名稱
    static final String TOPIC_EXCHAGE = "TOPIC_exchange";
    //隊列名稱
    static final String TOPIC_QUEUE_INSERT = "TOPIC_queue_insert";
    //隊列名稱
    static final String TOPIC_QUEUE_UPDATE = "TOPIC_queue_update";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*創建頻道*/
        Channel channel = connection.createChannel();
        /**
         * 聲明交換機
         * 參數1:交換機名稱
         * 參數2:交換機類型,fanout、topic、direct、headers
         */
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);
        /*聲明創建隊列*/
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(TOPIC_QUEUE_INSERT,true,false,false,null);
        channel.queueDeclare(TOPIC_QUEUE_UPDATE,true,false,false,null);
        /*隊列綁定交換機*/
        /*
         queue 隊列名稱

         exchange 交換機名稱

         routingKey 路由key
         */
        channel.queueBind(TOPIC_QUEUE_INSERT,TOPIC_EXCHAGE, "pjh.insert");
        channel.queueBind(TOPIC_QUEUE_UPDATE, TOPIC_EXCHAGE, "pjh.update");
        // 發送信息
        String message = "新增了商品。TOPIC模式;routing key 為 insert " ;
        /**
         * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
         * 參數2:路由key,簡單模式可以傳遞隊列名稱
         * 參數3:消息其它屬性
         * 參數4:消息內容
         */
        channel.basicPublish(TOPIC_EXCHAGE, "pjh.insert", null, message.getBytes());
        System.out.println("已發送消息:" + message);

        // 發送信息
        message = "修改了商品。TOPIC模式;routing key 為 update" ;
        /**
         * 參數1:交換機名稱,如果沒有指定則使用默認Default Exchage
         * 參數2:路由key,簡單模式可以傳遞隊列名稱
         * 參數3:消息其它屬性
         * 參數4:消息內容
         */
        channel.basicPublish(TOPIC_EXCHAGE, "pjh.update", null, message.getBytes());
        System.out.println("已發送消息:" + message);
        /*關閉資源*/
        channel.close();
        connection.close();
    }
}

消費者

package com.pjh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Topic_Consumer2 {
    //交換機名稱
    static final String TOPIC_EXCHAGE = "TOPIC_exchange";
    //隊列名稱
    static final String TOPIC_QUEUE_INSERT = "TOPIC_queue_insert";
    //隊列名稱
    static final String TOPIC_QUEUE_UPDATE = "TOPIC_queue_update";
    public static void main(String[] args) throws IOException, TimeoutException {
        /*使用工具類獲取連接*/
        Connection connection = ConnectionUtil.getConnection();
        /*獲取channel*/
        Channel channel = connection.createChannel();
        /*聲明交換機*/
        channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC);
        // 聲明(創建)隊列
        /**
         * 參數1:隊列名稱
         * 參數2:是否定義持久化隊列
         * 參數3:是否獨占本次連接
         * 參數4:是否在不使用的時候自動刪除隊列
         * 參數5:隊列其它參數
         */
        channel.queueDeclare(TOPIC_QUEUE_UPDATE,true,false,false,null);
        /*隊列綁定交換機*/
        channel.queueBind( TOPIC_QUEUE_UPDATE,TOPIC_EXCHAGE,"pjh.*");
        /*創建消費者并設置消息處理*/
       Consumer consumer = new DefaultConsumer(channel) {
            @Override
            /**
             * consumerTag 消息者標簽,在channel.basicConsume時候可以指定
             * envelope 消息包的內容,可從中獲取消息id,消息routingkey,交換機,消息和重傳標志(收到消息失敗后是否需要重新發送)
             * properties 屬性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key為:" + envelope.getRoutingKey());
                //交換機
                System.out.println("交換機為:" + envelope.getExchange());
                //消息id
                System.out.println("消息id為:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("消費者2-接收到的消息為:" + new String(body, "utf-8"));
            }
        };
        //監聽消息
        /**
         * 參數1:隊列名稱
         * 參數2:是否自動確認,設置為true為表示消息接收到自動向mq回復接收到了,mq接收到回復會刪除消息,設置為false則需要手動確認
         * 參數3:消息接收到后回調
         */
        channel.basicConsume(TOPIC_QUEUE_UPDATE,true,consumer);
    }
}

測試

成功接受消息

在這里插入圖片描述

模式總結

1、簡單模式 HelloWorld

一個生產者、一個消費者,不需要設置交換機(使用默認的交換機)

2、工作隊列模式 Work Queue

一個生產者、多個消費者(競爭關系),不需要設置交換機(使用默認的交換機)

3、發布訂閱模式 Publish/subscribe

需要設置類型為fanout的交換機,并且交換機和隊列進行綁定,當發送消息到交換機后,交換機會將消息發送到綁定的隊列

4、路由模式 Routing

需要設置類型為direct的交換機,交換機和隊列進行綁定,并且指定routing key,當發送消息到交換機后,交換機會根據routing key將消息發送到對應的隊列

5、通配符模式 Topic

需要設置類型為topic的交換機,交換機和隊列進行綁定,并且指定通配符方式的routing key,當發送消息到交換機后,交換機會根據routing key將消息發送到對應的隊列

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容