【RabbitMQ】——隊列模式(2)

        本篇博客接着上一篇介紹MQ的隊列模式。
訂閱模式
 
        P:生產者
        c1、c2:消費者
        紅色:消息隊列
        x:交換機


        這種模式猛地一看和Work模式很像,但是這個模式中的每個消費者都有自己的隊列,同時引入了一個新的概念——交換機。
        交互機分別綁定生產者和消息隊列,生產者發送的消息會經過交換機到達隊列,這樣可以實現一個消息被多個消費者獲取的目的。


注意
        如果交換機沒有綁定隊列,生產者發送的消息會丟失,因爲交互機沒有存儲消息的能力,消息只能存在隊列之中。



生產者代碼:
public class Send {
    private final static String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明交換機(exchange)
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");   
        String message = "Hello World"; // 消息內容
        // 將消息發送到交換機
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println(" [x] Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}


消費者一代碼:
public class Recv {

    private final static String QUEUE_NAME = "test_queue_fanout_1";
    private final static String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 綁定隊列到交換機
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        // 同一時刻服務器只會發一條消息給消費者
        channel.basicQos(1);
        // 定義隊列的消費者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 監聽隊列,手動返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 獲取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" 前臺系統: '" + message + "'");
            Thread.sleep(10);
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}


消費者二代碼:
public class Recv2 {

    private final static String QUEUE_NAME = "test_queue_fanout_2";
    private final static String EXCHANGE_NAME = "test_exchange_fanout";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 綁定隊列到交換機
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");


        // 同一時刻服務器只會發一條消息給消費者
        channel.basicQos(1);
        // 定義隊列的消費者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 監聽隊列,手動返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 獲取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" 搜索系統: '" + message + "'");
            Thread.sleep(10);
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

        以上消費者一和消費者二種分別聲明瞭自己的隊列,都綁定到了同一個在生成者端聲明的交換機中。
        生產者發送一條消息到交換機,綁定到了交換機的隊列都可以獲得這條消息,實現一條消息被多個消費者獲得的功能。


        在RabbitMQ的管理工具中可以查看綁定到交換機的隊列,如下:
 

路由模式
 


        路由模式中的元素和訂閱者模式中的元素一樣,但是路由模式是在訂閱者模式基礎上的升級。訂閱者模式中的隊列綁定了交換機後會接受生成者發送的所有消息,但是消費者可能並不想接受所有的消息,路由模式可以實現消息的訂製。
        路由模式在生產者發送消息時會聲明一個key,消費者的隊列在綁定交換機是會表明自己接受什麼key值的消息,下面來看一下代碼:


生產者代碼:

public class Send {
    private final static String EXCHANGE_NAME = "test_exchange_direct";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        // 消息內容
        String message1 = "刪除信息, id = 1001";
        String message2 = "更新信息, id = 1002";
        // 發送消息,聲明消息的key
        channel.basicPublish(EXCHANGE_NAME, "delete", null, message1.getBytes());
        channel.basicPublish(EXCHANGE_NAME, "update", null, message2.getBytes());
        System.out.println(" [x] Sent '" + message1 + "'");
        System.out.println(" [x] Sent '" + message2 + "'");
        channel.close();
        connection.close();
    }
}

消費者代碼:
public class Recv {
    private final static String QUEUE_NAME = "test_queue_direct_1";
    private final static String EXCHANGE_NAME = "test_exchange_direct";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();


        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 綁定隊列到交換機,聲明接受消息的key 
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "delete");
        // 同一時刻服務器只會發一條消息給消費者
        channel.basicQos(1);
        // 定義隊列的消費者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 監聽隊列,手動返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 獲取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" 前臺系統: '" + message + "'");
            Thread.sleep(10);
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

        生產者發送了兩條消息,一條key爲delete,另一條key爲update。在消費者端,隊列綁定交互機時聲明瞭delete這個key,這樣當前消費者只會接受到delete消息,不會接收到奧update消息。


通配符模式
 


        通配符模式中的元素和訂閱者模式及路由模式中的元素一樣,而通配符模式是在路由模式基礎上的升級。路由模式需要消費者端的隊列在綁定交換機時聲明完整的key,如果需要接受多個key的消息則需要綁定多條。通配符模式就類似模糊匹配,只需要聲明部分關鍵字即可。


通配符有兩種:# 和 *
        #:可以匹配一個或多個詞
        *:只能匹配一個詞
例:
        item.category.add,使用#寫爲item.#即可匹配到,使用*則需寫爲item.*.*


生產者代碼:
public class Send {
    private final static String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明exchange
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        // 消息內容
        String message1 = "刪除,id = 1001";
        String message2 = "更新,id = 1002";
        channel.basicPublish(EXCHANGE_NAME, "item.delete", null, message1.getBytes());
        channel.basicPublish(EXCHANGE_NAME, "item.update", null, message2.getBytes());
        System.out.println(" [x] Sent '" + message1 + "'");
        System.out.println(" [x] Sent '" + message2 + "'");
        channel.close();
        connection.close();
    }
}

消費者代碼:
public class Recv2 {
    private final static String QUEUE_NAME = "test_queue_topic_2";
    private final static String EXCHANGE_NAME = "test_exchange_topic";
    public static void main(String[] argv) throws Exception {
        // 獲取到連接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 綁定隊列到交換機
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "item.#");
        // 同一時刻服務器只會發一條消息給消費者
        channel.basicQos(1);
        // 定義隊列的消費者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 監聽隊列,手動返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 獲取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(" 搜索系統: '" + message + "'");
            Thread.sleep(10);
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

        消費者端的的隊列在綁定交換機時只需要寫item.#即可匹配到item.delete和item.update。

小結

        本篇文章中的三種消息隊列包含的元素相同,功能是越來越靈活的,之所以能實現這種效果,是因爲交換機有不同的模式,三種模式的生產者端在聲明交換機時分別使用了不同的模式。

        訂閱模式——Fanout

        路由模式——Direct
        通配模式——Topic
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章