RabbitMQ學習系列(四):發佈-訂閱模型詳解

(一)發佈-訂閱模型(Publish/Subscribe)

發佈訂閱模型的結構圖如下所示:

和前兩個的模型結構不同,在發佈訂閱模型中多了一個X(exchange),exchange是一個交換機,生產者不是直接將消息發送給隊列,而是先發送給交換機。消費者可以通過隊列去訂閱這個交換機,每個消費者對應於自己的一個隊列。

這個結構就好像我們訂閱微信公衆號一樣,作者將文章發送到自己的公衆號上,只有訂閱過該公衆號的人才能收到消息。因此這個模型被稱爲發佈-訂閱模型。

(二)發佈-訂閱模型實踐

發佈訂閱模型中多了交換機的存在,而我們在rabbitmq的可視化界面中就見到過exchange

繼續通過代碼展示該模型:

2.1 工具類

工具類和之前都一樣,不做介紹了

public class ConnectionUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        //定義一個連接工廠
        ConnectionFactory factory=new ConnectionFactory();
        //設置服務地址
        factory.setHost("127.0.0.1");
        //設置AMQP端口
        factory.setPort(5672);
        //設置VHOSTS
        factory.setVirtualHost("/vhosts_sdxb");
        //設置用戶名
        factory.setUsername("user_sdxb");
        factory.setPassword("123456");
        return factory.newConnection();
    }
}

2.2 生產者

public class Sent {
    private static final String EXCHANGE="ps_exchange";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //綁定交換機,設置類型爲fanout
        channel.exchangeDeclare(EXCHANGE,"fanout");
        String msg="hello world";
        channel.basicPublish(EXCHANGE,"",null,msg.getBytes());
        channel.close();
        connection.close();
    }
}

生產者在發佈訂閱模型中不再綁定隊列,而是綁定交換機。exchange的種類有4中,分別是Direct 、Fanout 、Topic、Headers。接下來會做詳細介紹。

2.3 消費者一

public class Receive1 {
    private static final String QUEUE_NAME="ps_queue1";
    private static final String EXCHANGE="ps_exchange";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //將隊列和交換機綁定
        channel.queueBind(QUEUE_NAME,EXCHANGE,"");
        //保證一次只分發一次
        channel.basicQos(1);
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg=new String(body,"utf-8");
                System.out.println(msg);
                //設置手動回執
                channel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //關閉自動回覆
        boolean autoAck=false;
        channel.basicConsume(QUEUE_NAME,autoAck,consumer);
    }
}

消費者的代碼和之前一樣,唯一的區別是增加了隊列和交換機的綁定channel.queueBind();

2.4 消費者二

public class Receive2 {
    private static final String QUEUE_NAME="ps_queue2";
    private static final String EXCHANGE="ps_exchange";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtil.getConnection();
        final Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //將隊列和交換機綁定
        channel.queueBind(QUEUE_NAME,EXCHANGE,"");
        //保證一次只分發一次
        channel.basicQos(1);
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg=new String(body,"utf-8");
                System.out.println(msg);
                //設置手動回執
                channel.basicAck(envelope.getDeliveryTag(),false);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //關閉自動回覆
        boolean autoAck=false;
        channel.basicConsume(QUEUE_NAME,autoAck,consumer);
    }
}

2.5 運行項目

由於此時rabbitmq中不存在名稱爲ps_exchange的交換機,因此我們可以手動在rabbitmq的可視化界面中創建,也可以運行一次生產者來創建交換機。接着運行兩個消費者和生產者,可以看到生產者發送出去的消息被消費者收到。

觀察此時的可視化界面,可以看到該交換機上已經綁定了兩個隊列:

(三)Exchange類型介紹

exchange的種類有4中,分別是Direct 、Fanout 、Topic、Headers。接下來會做詳細介紹。

3.1 Fanout(不處理路由鍵)

直接將消息路由到所有綁定的隊列中,無須對消息的routingkey進行匹配操作。即交換機將消息從生產者獲取之後,直接發給訂閱的隊列。

3.2 Direct(處理路由鍵)

交換機和隊列綁定時會設置路由鍵(routingkey),當消息從生產者發送給交換機時也會發送一個路由鍵。只有當這兩個路由鍵相同時,交換機纔會把消息發送給隊列。

3.3 Topic(可以有通配符)

Topic和Direct類似,只不過Direct要求路由鍵完全相同,但是Topic可以使用通配符進行匹配,如#,*

3.4 header(根據header匹配)

在發佈消息的時候就需要傳入header值,其中的header就是binding時的arguments參數

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