生產者
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進行消費。