rabbitMQ學習筆記(三)——發佈訂閱模式

介紹


首先來段官方文檔

In the previous tutorial we created a work queue. The assumption behind a work queue is that each task is delivered to exactly one worker. In this part we’ll do something completely different – we’ll deliver a message to multiple consumers. This pattern is known as “publish/subscribe”.

The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesn’t even know if a message will be delivered to any queue at all.

Instead, the producer can only send messages to an exchange. An exchange is a very simple thing. On one side it receives messages from producers and the other side it pushes them to queues. The exchange must know exactly what to do with a message it receives. Should it be appended to a particular queue? Should it be appended to many queues? Or should it get discarded. The rules for that are defined by the exchange type.

簡言之,之前的模式都是一個生產者,一個隊列,一個或者多個消費者。然而發佈訂閱模式引入了交換機的概念。有了交換機,生產者不會將消息直接發到隊列中,而是發送到交換機,由交換機來決定將消息傳送到具體哪一個或者多個隊列中。其模型如下:
在這裏插入圖片描述

生產者實現


其基本實現邏輯和第一節講的無異,關鍵是多了交換機和交換機與隊列之間的聯繫。因此在代碼上,僅僅添加了聲明交換機,和綁定隊列。注意,這裏聲明瞭兩個隊列,綁定時也是綁定了兩個隊列。
聲明交換機代碼如下:

//聲明一個交換機
//參數:String exchange, String type
/**
* 參數明細:
* exchange:交換機名稱
* type:交換機類型
*       fanout:對應額rabbitmq工作模式是publish/subscribe
*       direct:對應額rabbitmq工作模式是routing路由模式
*       topic:對應額rabbitmq工作模式是topics通配符
*       headers:對應額rabbitmq工作模式是headers轉發器
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);

綁定隊列代碼如下:

//進行交換機和隊列綁定
//參數:String queue, String exchange, String routingKey
/**
* 參數明細:
* queue:隊列名稱
* exchange:交換機名稱
* routingKey:路由key,作用是交換機根據路由key將消息發佈到指定隊列中。在發佈訂閱模式設爲空串
*/
channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");

完整代碼如下:

public class Producer01 {
    private static final String QUEUENAME01 = "queue01";
    private static final String QUEUENAME02 = "queue02";
    private static final String EXCHANGE_FANOUT_INFO = "exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //通過連接工廠創建新的連接和mq建立連接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //設置虛擬機,一個mq的服務可以設置多個虛擬機,每個虛擬機相當於一個獨立的mq。
        connectionFactory.setVirtualHost("/");
        //建立新連接
        Connection connection = null;
        connection = connectionFactory.newConnection();
        //創建會話通道,生產者和mq服務所有的通信都在channel裏完成。
        Channel channel = connection.createChannel();
        //聲明隊列:如果隊列在mq中沒有,則創建
        //參數String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        /**
         * 參數明細:
         * queue:隊列名稱
         * durable:持久化,如果持久化,重啓mq服務隊列還在
         * exclusive:是否獨佔連接,隊列只允許在該連接中訪問,如果連接關閉則隊列刪除,設置爲true,可用於臨時隊列。
         * autoDelete:自動刪除,設置爲true,連接不使用則刪除隊列
         * arguments:指定一些擴展參數。例如存活時間等等
         */
        channel.queueDeclare(QUEUENAME01,true,false,false,null);
        channel.queueDeclare(QUEUENAME02,true,false,false,null);
        //聲明一個交換機
        //參數:String exchange, String type
        /**
         * 參數明細:
         * exchange:交換機名稱
         * type:交換機類型
         *       fanout:對應額rabbitmq工作模式是publish/subscribe
         *       direct:對應額rabbitmq工作模式是routing路由模式
         *       topic:對應額rabbitmq工作模式是topics通配符
         *       headers:對應額rabbitmq工作模式是headers轉發器
         */
        channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);
        //進行交換機和隊列綁定
        //參數:String queue, String exchange, String routingKey
        /**
         * 參數明細:
         * queue:隊列名稱
         * exchange:交換機名稱
         * routingKey:路由key,作用是交換機根據路由key將消息發佈到指定隊列中。在發佈訂閱模式設爲空串
         */
        channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");
        channel.queueBind(QUEUENAME02,EXCHANGE_FANOUT_INFO,"");
        //發送消息
        //參數:String exchange, String routingKey, BasicProperties props, byte[] body
        /**
         * 參數明細:
         * exchange:交換機,不指定的話爲默認交換機
         * routingKey:交換機根據路由key,將消息轉發到指定隊列,如果使用默認交換機,routingKey設置爲隊列的名稱
         * props:消息的屬性
         * body:消息內容,字節數組形式
         */
        channel.basicPublish(EXCHANGE_FANOUT_INFO,"",null,"現在是發佈訂閱模式".getBytes());
        System.out.println("send to mq");

        //關閉通道
        channel.close();
        //關閉連接
        connection.close();
    }

}

消費者實現


消費者實現同樣也要聲明交換機和綁定隊列,這次只需要綁定一次(對應的)隊列即可。

public class Consumer01 {

    private static final String QUEUENAME01 = "queue01";
    private static final String EXCHANGE_FANOUT_INFO = "exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //通過連接工廠創建新的連接和mq建立連接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //設置虛擬機,一個mq的服務可以設置多個虛擬機,每個mq相當於一個獨立的mq。
        connectionFactory.setVirtualHost("/");
        //建立新連接
        Connection connection = null;
        connection = connectionFactory.newConnection();
        //創建會話通道,生產者和mq服務所有的通信都在channel裏完成。
        Channel channel = connection.createChannel();

        //聲明隊列:如果隊列在mq中沒有,則創建
        channel.queueDeclare(QUEUENAME01,true,false,false,null);

        channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);

        channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");

        //實現消費方法
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){

            /**
             * 當接收到消息後,此方法執行
             * @param consumerTag 消費者標籤,用來標識消費者,在監聽隊列時也可設置channel.basicConsume
             * @param envelope 信封,可用信封獲取一些信息,例如交換機、消息id等
             * @param properties 消息屬性,發送消息時設置的消息屬性可在這裏獲取
             * @param body 消息內容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //super.handleDelivery(consumerTag, envelope, properties, body);
                //拿到交換機
                String exchange = envelope.getExchange();
                //消息id,mq在channel中用來表示消息的id,可用於確認消息已接收。
                long deliveryTag = envelope.getDeliveryTag();
                String message = new String(body,"UTF-8");
                System.out.println("交換機:"+exchange+"  消息id"+deliveryTag+"  消息內容"+message);
            }
        };

        //監聽隊列
        //參數 String queue, boolean autoAck, Consumer callback
        /**
         * 參數明細:
         * queue:隊列名稱
         * autoAck:自動回覆,消費者接受到消息會自動告訴mq服務消息已接收此參數設置爲true
         *          會自動回覆mq,如果設置爲false要通過編程回覆,不回覆的話消息一直在隊列裏。
         * callback:回調方法,當消費者接收到消息要執行的方法。
         */
        channel.basicConsume(QUEUENAME01,true,defaultConsumer);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章