RabbitMq工作模式

1、 工作模式

RabbitMQ有以下幾種工作模式 :
1、Work queues
2、Publish/Subscribe
3、Routing
4、Topics
5、Header
6、RPC

1.1 Work queues(工作隊(duì)列)

image.png

work queues與入門程序相比,多了一個消費(fèi)端,兩個消費(fèi)端共同消費(fèi)同一個隊(duì)列中的消息。
應(yīng)用場景:對于 任務(wù)過重或任務(wù)較多情況使用工作隊(duì)列可以提高任務(wù)處理的速度。
測試:
1、使用入門程序,啟動多個消費(fèi)者。
2、生產(chǎn)者發(fā)送多個消息。

public class Producer01 {

    private static final String QUEUE = "helloworld";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = null;
        Channel channel = null;
        try {

        //創(chuàng)建連接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");//rabbitmq默認(rèn)虛擬機(jī)名稱為“/”,虛擬機(jī)相當(dāng)于一個獨(dú)立的mq服務(wù),一個mq有多個虛擬機(jī)

        //創(chuàng)建與RabbitMQ服務(wù)的TCP連接
         connection = connectionFactory.newConnection();
        //創(chuàng)建與Exchange的通道,每個連接可以創(chuàng)建多個通道,每個通道代表一個會話任務(wù)
         channel = connection.createChannel();
        /**
         * 聲明隊(duì)列,如果Rabbit中沒有此隊(duì)列將自動創(chuàng)建
         * QUEUE:隊(duì)列名稱
         * durable:是否持久化
         * exclusive:隊(duì)列是否獨(dú)占此鏈接
         * autoDelete:隊(duì)列不再使用時是否自動刪除此
         * arguments:隊(duì)列參數(shù)
         */
        channel.queueDeclare(QUEUE, true, false, false, null);

        String message = "helloworld小明"+System.currentTimeMillis();

        /**
         * 消息發(fā)布方法
         * exchange: Exchange的名稱,如果沒有指定,則使用Default Exchange
         * routingKey:routingKey,消息的路由Key,是用于Exchange(交換機(jī))將消息轉(zhuǎn)發(fā)到指定的消息隊(duì)列
         * props: 消息包含的屬性
         * body: 消息體
         * 這里沒有指定交換機(jī),消息將發(fā)送給默認(rèn)交換機(jī),每個隊(duì)列也會綁定那個默認(rèn)的交換機(jī),但是不能顯示綁定或解除綁定
         * 默認(rèn)的交換機(jī),routingKey等于隊(duì)列名稱
         */
        channel.basicPublish("", QUEUE, null, message.getBytes());
        System.out.println("Send Message is:'" + message + "'");

        }catch (Exception e){
            e.printStackTrace();
        }finally {

            if(channel != null)
            {
                channel.close();
            }
            if(connection != null)
            {
                connection.close();
            }
        }

    }

}

消費(fèi)者

public class Consumer01 {

    private static final String QUEUE = "helloworld";

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = null;
        Channel channel = null;

        //創(chuàng)建連接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");//rabbitmq默認(rèn)虛擬機(jī)名稱為“/”,虛擬機(jī)相當(dāng)于一個獨(dú)立的mq服務(wù),一個mq有多個虛擬機(jī)

        //創(chuàng)建與RabbitMQ服務(wù)的TCP連接
        connection = connectionFactory.newConnection();
        //創(chuàng)建與Exchange的通道,每個連接可以創(chuàng)建多個通道,每個通道代表一個會話任務(wù)
        channel = connection.createChannel();
        /**
         * 聲明隊(duì)列,如果Rabbit中沒有此隊(duì)列將自動創(chuàng)建
         * QUEUE:隊(duì)列名稱
         * durable:是否持久化
         * exclusive:隊(duì)列是否獨(dú)占此鏈接
         * autoDelete:隊(duì)列不再使用時是否自動刪除此
         * arguments:隊(duì)列參數(shù)
         */
        channel.queueDeclare(QUEUE, true, false, false, null);

        /**
         * 定義消費(fèi)方法
         */
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /**
             *
             * @param consumerTag 消費(fèi)者的標(biāo)簽,在channel.basicConsume()去指定
             * @param envelope 消息包的內(nèi)容,可從中獲取消息id,消息routingkey,交換機(jī),消息和重傳標(biāo)志(收到消息失敗后是否需要重新發(fā)送)
             * @param properties  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);
                //交換機(jī)
                String exchange = envelope.getExchange();
                //路由key
                String routingKey = envelope.getRoutingKey();
                //消息id
                long deliveryTag = envelope.getDeliveryTag();
                //消息內(nèi)容
                String msg = new String(body,"UTF-8");
                System.out.println("receive message.." + msg);


            }
        };

        /**
         *監(jiān)聽隊(duì)列String queue, boolean autoAck, Consumer callback
         * queue 隊(duì)列名稱
         * autoAck 是否自動回復(fù),設(shè)置為true為表示消息接收到自動向mq回復(fù)接收到了,mq接收到回復(fù)會刪除消息,設(shè)置為false則需要手動回復(fù)
         * callback 消費(fèi)消息的方法,消費(fèi)者接收到消息后調(diào)用此方法
         */
        channel.basicConsume(QUEUE,true,defaultConsumer);
    }

}

結(jié)果:
1、一條消息只會被一個消費(fèi)者接收;
2、rabbit采用輪詢的方式將消息是平均發(fā)送給消費(fèi)者的;
3、消費(fèi)者在處理完某條消息后,才會收到下一條消息。

1.2 Publish/subscribe(發(fā)布訂閱)

1.2.1 工作模式
image.png

案例:
用戶通知,當(dāng)用戶充值成功或轉(zhuǎn)賬完成系統(tǒng)通知用戶,通知方式有短信、郵件多種方法

1、生產(chǎn)者

聲明Exchange_fanout_inform交換機(jī)。
聲明兩個隊(duì)列并且綁定到此交換機(jī),綁定時不需要指定routingkey
發(fā)送消息時不需要指定routingkey

public class Producer02_publish {
    //隊(duì)列名稱
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";

    public static void main(String[] args) {
        //通過連接工廠創(chuàng)建新的連接和mq建立連接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//端口
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //設(shè)置虛擬機(jī),一個mq服務(wù)可以設(shè)置多個虛擬機(jī),每個虛擬機(jī)就相當(dāng)于一個獨(dú)立的mq
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try {
            //建立新連接
            connection = connectionFactory.newConnection();
            //創(chuàng)建會話通道,生產(chǎn)者和mq服務(wù)所有通信都在channel通道中完成
            channel = connection.createChannel();
            //聲明隊(duì)列,如果隊(duì)列在mq 中沒有則要創(chuàng)建
            //參數(shù):String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
            /**
             * 參數(shù)明細(xì)
             * 1、queue 隊(duì)列名稱
             * 2、durable 是否持久化,如果持久化,mq重啟后隊(duì)列還在
             * 3、exclusive 是否獨(dú)占連接,隊(duì)列只允許在該連接中訪問,如果connection連接關(guān)閉隊(duì)列則自動刪除,如果將此參數(shù)設(shè)置true可用于臨時隊(duì)列的創(chuàng)建
             * 4、autoDelete 自動刪除,隊(duì)列不再使用時是否自動刪除此隊(duì)列,如果將此參數(shù)和exclusive參數(shù)設(shè)置為true就可以實(shí)現(xiàn)臨時隊(duì)列(隊(duì)列不用了就自動刪除)
             * 5、arguments 參數(shù),可以設(shè)置一個隊(duì)列的擴(kuò)展參數(shù),比如:可設(shè)置存活時間
             */
            channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
            channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
            //聲明一個交換機(jī)
            //參數(shù):String exchange, String type
            /**
             * 參數(shù)明細(xì):
             * 1、交換機(jī)的名稱
             * 2、交換機(jī)的類型
             * fanout:對應(yīng)的rabbitmq的工作模式是 publish/subscribe
             * direct:對應(yīng)的Routing    工作模式
             * topic:對應(yīng)的Topics工作模式
             * headers: 對應(yīng)的headers工作模式
             */
            channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
            //進(jìn)行交換機(jī)和隊(duì)列綁定
            //參數(shù):String queue, String exchange, String routingKey
            /**
             * 參數(shù)明細(xì):
             * 1、queue 隊(duì)列名稱
             * 2、exchange 交換機(jī)名稱
             * 3、routingKey 路由key,作用是交換機(jī)根據(jù)路由key的值將消息轉(zhuǎn)發(fā)到指定的隊(duì)列中,在發(fā)布訂閱模式中調(diào)協(xié)為空字符串
             */
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_FANOUT_INFORM,"");
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");
            //發(fā)送消息
            //參數(shù):String exchange, String routingKey, BasicProperties props, byte[] body
            /**
             * 參數(shù)明細(xì):
             * 1、exchange,交換機(jī),如果不指定將使用mq的默認(rèn)交換機(jī)(設(shè)置為"")
             * 2、routingKey,路由key,交換機(jī)根據(jù)路由key來將消息轉(zhuǎn)發(fā)到指定的隊(duì)列,如果使用默認(rèn)交換機(jī),routingKey設(shè)置為隊(duì)列的名稱
             * 3、props,消息的屬性
             * 4、body,消息內(nèi)容
             */
            for(int i=0;i<5;i++){
                //消息內(nèi)容
                String message = "send inform message to user";
                channel.basicPublish(EXCHANGE_FANOUT_INFORM,"",null,message.getBytes());
                System.out.println("send to mq "+message);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //關(guān)閉連接
            //先關(guān)閉通道
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

2、郵件發(fā)送消費(fèi)者
public class Consumer02_subscribe_sms {

    //隊(duì)列名稱
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_FANOUT_INFORM="exchange_fanout_inform";
    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = null;
        Channel channel = null;

        //創(chuàng)建連接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");//rabbitmq默認(rèn)虛擬機(jī)名稱為“/”,虛擬機(jī)相當(dāng)于一個獨(dú)立的mq服務(wù),一個mq有多個虛擬機(jī)

        //創(chuàng)建與RabbitMQ服務(wù)的TCP連接
        connection = connectionFactory.newConnection();
        //創(chuàng)建與Exchange的通道,每個連接可以創(chuàng)建多個通道,每個通道代表一個會話任務(wù)
        channel = connection.createChannel();

        /**
         * String exchange, String type
         * exchange:交換機(jī)名字
         * type 交換機(jī)類型
         */

        channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);

        /**
         * 聲明隊(duì)列,如果Rabbit中沒有此隊(duì)列將自動創(chuàng)建
         * QUEUE:隊(duì)列名稱
         * durable:是否持久化
         * exclusive:隊(duì)列是否獨(dú)占此鏈接
         * autoDelete:隊(duì)列不再使用時是否自動刪除此
         * arguments:隊(duì)列參數(shù)
         */
        channel.queueDeclare(QUEUE_INFORM_SMS, true, false, false, null);

        /**
         * 交換機(jī)和隊(duì)列綁定
         * String queue, String exchange, String routingKey
         * queue 隊(duì)列名稱
         * exchange 交換機(jī)名稱
         * routingKey 路由key
         */
        channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_FANOUT_INFORM,"");

        /**
         * 定義消費(fèi)方法
         */
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /**
             *
             * @param consumerTag 消費(fèi)者的標(biāo)簽,在channel.basicConsume()去指定
             * @param envelope 消息包的內(nèi)容,可從中獲取消息id,消息routingkey,交換機(jī),消息和重傳標(biāo)志(收到消息失敗后是否需要重新發(fā)送)
             * @param properties  properties
             * @param body
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //交換機(jī)
                String exchange = envelope.getExchange();
                //消息id,mq在channel中用來標(biāo)識消息的id,可用于確認(rèn)消息已接收
                long deliveryTag = envelope.getDeliveryTag();
                //消息內(nèi)容
                String message= new String(body,"utf-8");
                System.out.println("receive message:"+message);

            }
        };

        /**
         *監(jiān)聽隊(duì)列String queue, boolean autoAck, Consumer callback
         * queue 隊(duì)列名稱
         * autoAck 是否自動回復(fù),設(shè)置為true為表示消息接收到自動向mq回復(fù)接收到了,mq接收到回復(fù)會刪除消息,設(shè)置為false則需要手動回復(fù)
         * callback 消費(fèi)消息的方法,消費(fèi)者接收到消息后調(diào)用此方法
         */
        channel.basicConsume(QUEUE_INFORM_SMS,true,defaultConsumer);
    }

}

按照上邊的代碼,編寫郵件通知的消費(fèi)代碼。
3、短信發(fā)送消費(fèi)者
參考上邊的郵件發(fā)送消費(fèi)者代碼編寫.

1.2.4 思考

1、publish/subscribe與work queues有什么區(qū)別。

區(qū)別:
1)work queues不用定義交換機(jī),而publish/subscribe需要定義交換機(jī)。

2)publish/subscribe的生產(chǎn)方是面向交換機(jī)發(fā)送消息,work queues的生產(chǎn)方是面向隊(duì)列發(fā)送消息(底層使用默認(rèn)交換機(jī))。

3)publish/subscribe需要設(shè)置隊(duì)列和交換機(jī)的綁定,work queues不需要設(shè)置,實(shí)質(zhì)上work queues會將隊(duì)列綁定到默認(rèn)的交換機(jī) 。

相同點(diǎn):

1、所以兩者實(shí)現(xiàn)的發(fā)布/訂閱的效果是一樣的,多個消費(fèi)端監(jiān)聽同一個隊(duì)列不會重復(fù)消費(fèi)消息。

2、實(shí)質(zhì)工作用什么 publish/subscribe還是work queues。
建議使用 publish/subscribe,發(fā)布訂閱模式比工作隊(duì)列模式更強(qiáng)大,并且發(fā)布訂閱模式可以指定自己專用的交換機(jī)。

1.3 Routing()

1.3.1 工作模式
image.png

路由模式:
1、每個消費(fèi)者監(jiān)聽自己的隊(duì)列,并且設(shè)置routingkey。
2、生產(chǎn)者將消息發(fā)給交換機(jī),由交換機(jī)根據(jù)routingkey來轉(zhuǎn)發(fā)消息到指定的隊(duì)列

1.3.2代碼
1、生產(chǎn)者

聲明exchange_routing_inform交換機(jī)。
聲明兩個隊(duì)列并且綁定到此交換機(jī),綁定時需要指定routingkey
發(fā)送消息時需要指定routingkey

public class Producer03_routing {

    //隊(duì)列名稱
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";
    private static final String ROUTINGKEY_EMAIL="inform_email";
    private static final String ROUTINGKEY_SMS="inform_sms";

    public static void main(String[] args) {
        //通過連接工廠創(chuàng)建新的連接和mq建立連接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//端口
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //設(shè)置虛擬機(jī),一個mq服務(wù)可以設(shè)置多個虛擬機(jī),每個虛擬機(jī)就相當(dāng)于一個獨(dú)立的mq
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try {
            //建立新連接
            connection = connectionFactory.newConnection();
            //創(chuàng)建會話通道,生產(chǎn)者和mq服務(wù)所有通信都在channel通道中完成
            channel = connection.createChannel();
            //聲明隊(duì)列,如果隊(duì)列在mq 中沒有則要創(chuàng)建
            //參數(shù):String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
            /**
             * 參數(shù)明細(xì)
             * 1、queue 隊(duì)列名稱
             * 2、durable 是否持久化,如果持久化,mq重啟后隊(duì)列還在
             * 3、exclusive 是否獨(dú)占連接,隊(duì)列只允許在該連接中訪問,如果connection連接關(guān)閉隊(duì)列則自動刪除,如果將此參數(shù)設(shè)置true可用于臨時隊(duì)列的創(chuàng)建
             * 4、autoDelete 自動刪除,隊(duì)列不再使用時是否自動刪除此隊(duì)列,如果將此參數(shù)和exclusive參數(shù)設(shè)置為true就可以實(shí)現(xiàn)臨時隊(duì)列(隊(duì)列不用了就自動刪除)
             * 5、arguments 參數(shù),可以設(shè)置一個隊(duì)列的擴(kuò)展參數(shù),比如:可設(shè)置存活時間
             */
            channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
            channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
            //聲明一個交換機(jī)
            //參數(shù):String exchange, String type
            /**
             * 參數(shù)明細(xì):
             * 1、交換機(jī)的名稱
             * 2、交換機(jī)的類型
             * fanout:對應(yīng)的rabbitmq的工作模式是 publish/subscribe
             * direct:對應(yīng)的Routing    工作模式
             * topic:對應(yīng)的Topics工作模式
             * headers: 對應(yīng)的headers工作模式
             */
            channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
            //進(jìn)行交換機(jī)和隊(duì)列綁定
            //參數(shù):String queue, String exchange, String routingKey
            /**
             * 參數(shù)明細(xì):
             * 1、queue 隊(duì)列名稱
             * 2、exchange 交換機(jī)名稱
             * 3、routingKey 路由key,作用是交換機(jī)根據(jù)路由key的值將消息轉(zhuǎn)發(fā)到指定的隊(duì)列中,在發(fā)布訂閱模式中調(diào)協(xié)為空字符串
             */
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_SMS);
            //發(fā)送消息
            //參數(shù):String exchange, String routingKey, BasicProperties props, byte[] body
            /**
             * 參數(shù)明細(xì):
             * 1、exchange,交換機(jī),如果不指定將使用mq的默認(rèn)交換機(jī)(設(shè)置為"")
             * 2、routingKey,路由key,交換機(jī)根據(jù)路由key來將消息轉(zhuǎn)發(fā)到指定的隊(duì)列,如果使用默認(rèn)交換機(jī),routingKey設(shè)置為隊(duì)列的名稱
             * 3、props,消息的屬性
             * 4、body,消息內(nèi)容
             */
              for(int i=0;i<5;i++){
                //發(fā)送消息的時候指定routingKey
                String message = "send email inform message to user";
                channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL,null,message.getBytes());
                System.out.println("send to mq "+message);
            }
            for(int i=0;i<5;i++){
                //發(fā)送消息的時候指定routingKey
                String message = "send email inform message to user";
                channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTINGKEY_SMS,null,message.getBytes());
                System.out.println("send to mq "+message);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //關(guān)閉連接
            //先關(guān)閉通道
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

2、郵件發(fā)送消費(fèi)者

public class Consumer03_routing_email {

    //隊(duì)列名稱
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";
    private static final String ROUTINGKEY_EMAIL="inform_email";

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = null;
        Channel channel = null;

        //創(chuàng)建連接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");//rabbitmq默認(rèn)虛擬機(jī)名稱為“/”,虛擬機(jī)相當(dāng)于一個獨(dú)立的mq服務(wù),一個mq有多個虛擬機(jī)

        //創(chuàng)建與RabbitMQ服務(wù)的TCP連接
        connection = connectionFactory.newConnection();
        //創(chuàng)建與Exchange的通道,每個連接可以創(chuàng)建多個通道,每個通道代表一個會話任務(wù)
        channel = connection.createChannel();

        /**
         * String exchange, String type
         * exchange:交換機(jī)名字
         * type 交換機(jī)類型
         */

        channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);

        /**
         * 聲明隊(duì)列,如果Rabbit中沒有此隊(duì)列將自動創(chuàng)建
         * QUEUE:隊(duì)列名稱
         * durable:是否持久化
         * exclusive:隊(duì)列是否獨(dú)占此鏈接
         * autoDelete:隊(duì)列不再使用時是否自動刪除此
         * arguments:隊(duì)列參數(shù)
         */
        channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);

        /**
         * 交換機(jī)和隊(duì)列綁定
         * String queue, String exchange, String routingKey
         * queue 隊(duì)列名稱
         * exchange 交換機(jī)名稱
         * routingKey 路由key
         */
        channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);

        /**
         * 定義消費(fèi)方法
         */
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            /**
             *
             * @param consumerTag 消費(fèi)者的標(biāo)簽,在channel.basicConsume()去指定
             * @param envelope 消息包的內(nèi)容,可從中獲取消息id,消息routingkey,交換機(jī),消息和重傳標(biāo)志(收到消息失敗后是否需要重新發(fā)送)
             * @param properties  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);
                //交換機(jī)
                String exchange = envelope.getExchange();
                //消息id,mq在channel中用來標(biāo)識消息的id,可用于確認(rèn)消息已接收
                long deliveryTag = envelope.getDeliveryTag();
                //消息內(nèi)容
                String message= new String(body,"utf-8");
                System.out.println("receive message:"+message);
            }
        };

        /**
         *監(jiān)聽隊(duì)列String queue, boolean autoAck, Consumer callback
         * queue 隊(duì)列名稱
         * autoAck 是否自動回復(fù),設(shè)置為true為表示消息接收到自動向mq回復(fù)接收到了,mq接收到回復(fù)會刪除消息,設(shè)置為false則需要手動回復(fù)
         * callback 消費(fèi)消息的方法,消費(fèi)者接收到消息后調(diào)用此方法
         */
        channel.basicConsume(QUEUE_INFORM_EMAIL,true,defaultConsumer);
    }

}

3、短信發(fā)送消費(fèi)者
參考郵件發(fā)送消費(fèi)者的代碼流程,編寫短信通知的代碼

1.4.4思考

1、Routing模式和Publish/subscibe有啥區(qū)別?
Routing模式要求隊(duì)列在綁定交換機(jī)時要指定routingkey,消息會轉(zhuǎn)發(fā)到符合routingkey的隊(duì)列。

1.4 Topics(通配符模式)

1.4.1工作模式

image.png

路由模式:
1、每個消費(fèi)者監(jiān)聽自己的隊(duì)列,并且設(shè)置帶統(tǒng)配符的routingkey。
2、生產(chǎn)者將消息發(fā)給broker,由交換機(jī)根據(jù)routingkey來轉(zhuǎn)發(fā)消息到指定的隊(duì)列。

1.4.2代碼

案例:
根據(jù)用戶的通知設(shè)置去通知用戶,設(shè)置接收Email的用戶只接收Email,設(shè)置接收sms的用戶只接收sms,設(shè)置兩種通知類型都接收的則兩種通知都有效

1、生產(chǎn)者

聲明交換機(jī),指定topic類型:

1、每個消費(fèi)者監(jiān)聽自己的隊(duì)列,并且設(shè)置帶統(tǒng)配符的routingkey。
2、生產(chǎn)者將消息發(fā)給broker,由交換機(jī)根據(jù)routingkey來轉(zhuǎn)發(fā)消息到指定的隊(duì)列

1.4.2代碼
案例:
根據(jù)用戶的通知設(shè)置去通知用戶,設(shè)置接收Email的用戶只接收Email,設(shè)置接收sms的用戶只接收sms,設(shè)置兩種通知類型都接收的則兩種通知都有效。

1、生產(chǎn)者
聲明交換機(jī),指定topic類型:

public class Producer04_topics {

    //隊(duì)列名稱
    private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";
    private static final String QUEUE_INFORM_SMS = "queue_inform_sms";
    private static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";
    private static final String ROUTINGKEY_EMAIL="inform.#.email.#";
    private static final String ROUTINGKEY_SMS="inform.#.sms.#";

    public static void main(String[] args) {
        //通過連接工廠創(chuàng)建新的連接和mq建立連接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//端口
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //設(shè)置虛擬機(jī),一個mq服務(wù)可以設(shè)置多個虛擬機(jī),每個虛擬機(jī)就相當(dāng)于一個獨(dú)立的mq
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        try {
            //建立新連接
            connection = connectionFactory.newConnection();
            //創(chuàng)建會話通道,生產(chǎn)者和mq服務(wù)所有通信都在channel通道中完成
            channel = connection.createChannel();
            //聲明隊(duì)列,如果隊(duì)列在mq 中沒有則要創(chuàng)建
            //參數(shù):String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
            /**
             * 參數(shù)明細(xì)
             * 1、queue 隊(duì)列名稱
             * 2、durable 是否持久化,如果持久化,mq重啟后隊(duì)列還在
             * 3、exclusive 是否獨(dú)占連接,隊(duì)列只允許在該連接中訪問,如果connection連接關(guān)閉隊(duì)列則自動刪除,如果將此參數(shù)設(shè)置true可用于臨時隊(duì)列的創(chuàng)建
             * 4、autoDelete 自動刪除,隊(duì)列不再使用時是否自動刪除此隊(duì)列,如果將此參數(shù)和exclusive參數(shù)設(shè)置為true就可以實(shí)現(xiàn)臨時隊(duì)列(隊(duì)列不用了就自動刪除)
             * 5、arguments 參數(shù),可以設(shè)置一個隊(duì)列的擴(kuò)展參數(shù),比如:可設(shè)置存活時間
             */
            channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
            channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
            //聲明一個交換機(jī)
            //參數(shù):String exchange, String type
            /**
             * 參數(shù)明細(xì):
             * 1、交換機(jī)的名稱
             * 2、交換機(jī)的類型
             * fanout:對應(yīng)的rabbitmq的工作模式是 publish/subscribe
             * direct:對應(yīng)的Routing    工作模式
             * topic:對應(yīng)的Topics工作模式
             * headers: 對應(yīng)的headers工作模式
             */
            channel.exchangeDeclare(EXCHANGE_TOPICS_INFORM, BuiltinExchangeType.TOPIC);
            //進(jìn)行交換機(jī)和隊(duì)列綁定
            //參數(shù):String queue, String exchange, String routingKey
            /**
             * 參數(shù)明細(xì):
             * 1、queue 隊(duì)列名稱
             * 2、exchange 交換機(jī)名稱
             * 3、routingKey 路由key,作用是交換機(jī)根據(jù)路由key的值將消息轉(zhuǎn)發(fā)到指定的隊(duì)列中,在發(fā)布訂閱模式中調(diào)協(xié)為空字符串
             */
            channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_EMAIL);
            channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_TOPICS_INFORM,ROUTINGKEY_SMS);
            //發(fā)送消息
            //參數(shù):String exchange, String routingKey, BasicProperties props, byte[] body
            /**
             * 參數(shù)明細(xì):
             * 1、exchange,交換機(jī),如果不指定將使用mq的默認(rèn)交換機(jī)(設(shè)置為"")
             * 2、routingKey,路由key,交換機(jī)根據(jù)路由key來將消息轉(zhuǎn)發(fā)到指定的隊(duì)列,如果使用默認(rèn)交換機(jī),routingKey設(shè)置為隊(duì)列的名稱
             * 3、props,消息的屬性
             * 4、body,消息內(nèi)容
             */
            for(int i=0;i<5;i++){
                //發(fā)送消息的時候指定routingKey
                String message = "send email inform message to user";
                channel.basicPublish(EXCHANGE_TOPICS_INFORM,ROUTINGKEY_EMAIL,null,message.getBytes());
                System.out.println("send to mq "+message);
            }
          /*  for(int i=0;i<5;i++){
                //發(fā)送消息的時候指定routingKey
                String message = "send email inform message to user";
                channel.basicPublish(EXCHANGE_TOPICS_INFORM,ROUTINGKEY_SMS,null,message.getBytes());
                System.out.println("send to mq "+message);
            }*/

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //關(guān)閉連接
            //先關(guān)閉通道
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
2、消費(fèi)端

隊(duì)列綁定交換機(jī)指定通配符:
統(tǒng)配符規(guī)則:
中間以“.”分隔。
符號#可以匹配多個詞,符號*可以匹配一個詞語。

1.4.4思考

1、本案例的需求使用Routing工作模式能否實(shí)現(xiàn)?
使用Routing模式也可以實(shí)現(xiàn)本案例,共設(shè)置三個 routingkey,分別是email、sms、all,email隊(duì)列綁定email和all,sms隊(duì)列綁定sms和all,這樣就可以實(shí)現(xiàn)上邊案例的功能,實(shí)現(xiàn)過程比topics復(fù)雜。
Topic模式更多加強(qiáng)大,它可以實(shí)現(xiàn)Routing、publish/subscirbe模式的功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內(nèi)容

  • 來源 RabbitMQ是用Erlang實(shí)現(xiàn)的一個高并發(fā)高可靠AMQP消息隊(duì)列服務(wù)器。支持消息的持久化、事務(wù)、擁塞控...
    jiangmo閱讀 10,377評論 2 34
  • http://liuxing.info/2017/06/30/Spring%20AMQP%E4%B8%AD%E6%...
    sherlock_6981閱讀 15,968評論 2 11
  • RabbitMQ 簡介 MQ 消息隊(duì)列,上承生產(chǎn)者,下接消費(fèi)者。從生產(chǎn)者側(cè)獲取消息,然后將消息轉(zhuǎn)發(fā)給消費(fèi)者。由此可...
    2205閱讀 3,522評論 1 11
  • RabbitMQ的工作隊(duì)列模式 1.Work queues——工作隊(duì)列模式 1、一條消息只會被一個消費(fèi)者接收; ...
    李小二的倔強(qiáng)閱讀 334評論 0 0
  • 1. 歷史 RabbitMQ是一個由erlang開發(fā)的AMQP(Advanced Message Queue )的...
    高廣超閱讀 6,108評論 3 51