RabbitMq創建生產者和消費者例子

 

生產者

package com.ly.liyong.rabbitmq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class PublisherController {
    private static final String EXCHANGE_NAME = "exchange_ly_demo";
    private static final String ROUTING_KEY = "routing_ly_demo";
    private static final String QUEUE_NAME = "queue_ly_demo";
    private static final String IP_ADDRESS = "47.105.121.99";
    private static final int PORT = 5672;

    public static void main(String[] args) throws IOException,
            TimeoutException, InterruptedException {
        //定義連接工廠
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername("lpadmin");
        factory.setPassword("lpadmin");
        //創建連接
        Connection connection = factory.newConnection();
        //創建信道
        Channel channel = connection.createChannel();
        //direct模式的持久化、非自動刪除的交換器
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
        //持久化、非排他的、非自動刪除的隊列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //將交換器與隊列通過路由鍵綁定
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

        for (int i = 1; i < 5; i++) {
            //發送一條持久的消息
            String msg = "大帥哥,你好!" + i;
            byte[] msgByte = msg.getBytes();
            System.out.println("send: " + msg);
//            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, msgByte);

            //設置消息相關屬性
            //delivery_mode設置爲2,即消息會被持久化(即存入磁盤)在服務器中
            //priority設置這條消息的優先級爲0
//            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
//                    new AMQP.BasicProperties.Builder()
//                            .contentType("text/plain")
//                            .deliveryMode(2)
//                            .priority(1)
//                            .build(), msgByte);

            //發送帶有headers的消息,並設置消息過期時間爲10s
            Map<String, Object> headers = new HashMap<String, Object>();
            headers.put("localtion", "here");
            headers.put("time", "tody");
            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
                    new AMQP.BasicProperties.Builder()
                            .headers(headers)
                            .expiration("10000")
                            .build(), msgByte);
        }
        //關閉資源
        channel.close();
        connection.close();
    }
}

 

basicPublish()中特殊參數講解:

mandatory:參數爲true時,交換器無法找到對應的隊列時,RabbitMq會調用Basic.Return命令將消息返回給生產者(客戶端可通過ReturnListener監聽這個事件)。爲false時,消息直接被丟棄。(當備份交換器一起使用的時候,此參數設置失效)

immediate:參數爲true時,如果交換器在將消息路由到隊列上並不存在任何消費者,那麼這條消息將不會存入隊列中。當路由鍵匹配的所有隊列都麼有消費者時,該消息會通過Basic.Return返回至生產者(RabbitMq3.0版本開始去掉了對immediate參數的支持,原因是此參數會影響鏡像隊列的性能,增加了代碼複雜性,建議採用TTL和DLX的方法替代)

 

TTL:

1.隊列過期時間和消息過期時間

2.以上兩個同時使用時,則消息的過期時間以兩者之間較小的那個數值爲準

3.如果將消息的過期時間設置爲0,則表示除非此時可以直接將消息投遞到消費者,否則改消息會被立即丟棄(此屬性可以代替immediate參數)

4.隊列的過期刪除不能保證很及時。在RabbitMq重啓後,持久化的隊列的過期時間會被重新計算切TTL不能設置爲0

 

消費者(推模式)

package com.ly.liyong.rabbitmq;

import com.rabbitmq.client.*;

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

public class ConsumerController {
    private static final String QUEUE_NAME="queue_ly_demo";
    private static final String IP_ADDRESS="47.105.121.99";
    private static final int PORT=5672;

    public static void main(String[] args) throws IOException,
            TimeoutException, InterruptedException {
        Address[] addresses = new Address[]{
                new Address(IP_ADDRESS, PORT)
        };
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("lpadmin");
        factory.setPassword("lpadmin");
        //創建連接
        Connection connection = factory.newConnection(addresses);
        //創建信道
        final Channel channel = connection.createChannel(50);
        //設置客戶端最多接收未被ack的消息的個數
        channel.basicQos(64);
        //推模式
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                throws IOException {
                System.out.println("推模式: " + new String(body));
                //1s後消費
                try{
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //手動ack確認消費
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //消費模式
        channel.basicConsume(QUEUE_NAME, consumer);

        //拉模式(每次只拉取一條消息)
//        GetResponse response = channel.basicGet(QUEUE_NAME, false);
//        System.out.println("拉模式: " + new String(response.getBody()));
//        channel.basicAck(response.getEnvelope().getDeliveryTag(), false);

        //請求Broker重新發送未被確認的消息(1次)
//        channel.basicRecover();
        //等待回調函數執行完畢之後,關閉資源
//        TimeUnit.SECONDS.sleep(5);
//        channel.close();
//        connection.close();
    }
}

basicConsume()

queue:隊列名

autoAck:設置是否自動確認。建議設置false,即不自動確認

consumerTag:消費者標籤,用來區分多個消費者

noLocal:設置爲true則表示不能將同一個Connection中生產者發送的消息傳遞給這個Connection中的消費者

exclusive:是否排他

arguments:設置消費者的其他參數

callback:設置消費者的回調函數。用來處理RabbitMq推送過來的消息,比如DefaultConsumer,使用時需要客戶端重寫(override)其中的方法。

 

注意:

1.和生產者一樣,消費者客戶端同樣需要考慮線程安全的問題。消費者客戶端的這些callback會被分配到Channel不同的線程池上,這意味着消費者客戶端可以安全第調用這些阻塞方法,比如:channel.queueDeclare()、channel.basicCancel()等

2.每個Channel都擁有自己獨立的線程。最常用的做法是一個Channel對應一個消費者,也就是意味着消費者彼此之間沒有任何關聯。當然也可以在一個Channel鍾維持多個消費者,但是要注意一個問題,如果Channel中一個消費者一直運行,那麼其他消費者的callback會被“耽擱”。

3.在投遞(推)模式期間,RabbitMq會不斷地推送消息給消費者,當然推送消息的個數還是會收到Basic.Qos的限制。

4.如果只想從隊列獲得單條消息而不是持續訂閱,建議還是使用Basic.Get進行消費。

 

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