// 連接RabbitMQ 簡單工具類
public static Connection getConnection() throws Exception {
//1. 定義一個連接工廠
ConnectionFactory factory = new ConnectionFactory();
//2. 獲取服務地址
factory.setHost("127.0.0.1");
//3. AMQP 5672
factory.setPort(5672);
//4. vhost
factory.setVirtualHost("/test");
//5. 用戶名
factory.setUsername("test");
//6. 密碼
factory.setPassword("test");
return factory.newConnection();
}
// 獲取連接,生成通道
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
1)simple - 簡單隊列
涉及3個對象: 生產者 隊列 RabbitMQ 消費者
生產者:
// 申明隊列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
// 推送信息
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
消費者:
// 申明隊列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer consumer = new DefaultConsumer(channel) {
//獲取到達的消息
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "utf-8");
// 消費消息
System.out.println("new api recv: " + msg);
}
};
//4. 監聽隊列
channel.basicConsume(QUEUE_NAME,true,consumer);
缺點:耦合性高,生產者一 一對應消費者。
2)work queues - 工作隊列
channel.basicQos(1); //保證每次只分發一個
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
autoAck true 代表自動應答,
false代表需要如下手動應答:
channel.basicAck(envelope.getDeliveryTag(),false);
輪詢分發:自動應答,任務消息總是均勻分配
公平分發:手動應答,能者多勞
3)消息持久化
Boolean durable = true;// 持久化,注意在已存在的隊列上修改是不行的,可以刪除原有隊列或重新生成隊列。
channel.queueDeclare(QUEUE_NAME,durable ,false,false,null);
4)訂閱模式 publish/subscribe
- 一個生產者,多個消費者
- 每一個消費者都有自己的隊列
- 生產者沒有直接把消息發送到隊列 而是發送到了交換機 轉發器(exchange)
- 每個隊列都要綁定到交換機上,
- 生產者發送的消息 經過交換機 達到隊列 就能實現一個消息被多個消費者消費
生產者:
channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); // 聲明交換機,"fanout"爲分發
消費者:
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");//綁定隊列到交換機轉發器
5)Exchange(交換機 轉發器)
一方面是接收生產者的消息,另一方面是向隊列推送消息。
匿名轉發 ""
fanout(不處理路由鍵)
direct (處理路由器)
6)路由模式
生產者:
channel.exchangeDeclare(EXCHANGE_NAME,"direct"); // 聲明交換機
String routingKey = "info"; // error、warn.. 綁定路由
channel.basicPublish(EXCHANGE_NAME,routingKey ,null,msg.getBytes());
消費者:
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info");// 消費者綁定到路由
7)事務機制
txSelect txCommit txRollback
txSelect:用戶將當前 channel 設置成 transaction 模式
txCommit :用於提交事務;
txRollback:回滾事務。
這種模式很耗時,採用這種方式,降低了 RabbitMQ 的消息吞吐量
8)Conform 模式
生產者端 conform 模式的實現原理:
生產者將信道設置成 conform 模式,一旦信道進入 conform 模式,所有在該信道上面發佈的消息都會被指派一個唯一的 ID(從 1 開始),一旦消息被投遞到所有匹配的隊列之後,broker 就會發送一個確認給生產者(包含消息的唯一ID),這就使得生產者知道消息已經到達目的隊列了,如果消息和隊列是持久化的,那麼確認消息會將消息寫入磁盤之後發出,broker 也可以設置 basic.ack 的 multiple 域,表示到這個序列號之前的所有消息都已經得到了處理。
Conform 模式最大的好處就是他是異步。
開啓 conform 模式 Channel.conformSelect()
編程模式:
①普通 發一條 waitForConfirms()
②批量的 發一批 channel.waitForConfirmsOrDie(); //直到所有信息都發布,只要有一個未確認就會IOException
③異步 conform 模式:提供一個 回調方法
異步模式:
channel 對象提供的 ConfirmListener() 回調方法只包含 deliveryTag (當前 channel 發出的消息序列),我們需要自己爲每一個 Channel 維護一個 unconfirm 的消息序號集合,每 publish 一條數據,集合中元素加 1 ,每回調一次 handleAck 方法,unconfirm 集合刪掉相應的一條(multiple = false)或多條(multiple = true)記錄。從程序運行效率上看,這個 unconfirm 集合最好採用有序集合 SortedSed 存儲結構。
例如:
SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if (multiple) {
confirmSet.headSet(deliveryTag + 1L).clear();
} else {
confirmSet.remove(deliveryTag);
}
}
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if (multiple) {
confirmSet.headSet(deliveryTag + 1L).clear();
} else {
confirmSet.remove(deliveryTag);
}
}
});