MQ:Message Queue,消息隊列,是系統和系統之間的通信方法。
RabbitMQ是MQ的一種實現,較爲常用的還有ActiveMQ,Kafka(分佈式發佈訂閱消息系統)等。本篇博客是RabbitMQ的入門,簡單的瞭解一些RabbitMQ的5中隊列,分別爲:簡單隊列、work模式、訂閱模式、路由模式、通配符模式。
環境:windows上安裝RabbitMQ
工具類:用來獲取連接的,下面所有隊列的中獲取連接時使用,具體實現如下
public class ConnectionUtil {
publicstatic Connection getConnection() throws Exception {
//定義連接工廠
ConnectionFactory factory = new ConnectionFactory();
//設置服務地址
factory.setHost("localhost");
//端口
factory.setPort(5672);
//設置賬號信息,用戶名、密碼、vhost
factory.setVirtualHost("/test");
factory.setUsername("test");
factory.setPassword("test");
// 通過工程獲取連接
Connection connection = factory.newConnection();
return connection;
}
}
簡單隊列:
P:消息生成者
C:消息消費者
紅色:消息隊列
- 最簡單的模式,生產者將消息發送到隊列,消費者從隊列中獲取消息。
- 一條消息只能被消費一次,消費完畢後消息被刪除。
生產者代碼:
public class Send {
private final static String QUEUE_NAME = "test_queue";
public static void main(String[] argv) throws Exception {
// 獲取到連接以及mq通道,ConnectionUtil爲封裝的工具類
Connection connection = ConnectionUtil.getConnection();
// 從連接中創建通道
Channel channel = connection.createChannel();
// 聲明(創建)隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息內容
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null,message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//關閉通道和連接
channel.close();
connection.close();
}
}
啓動完畢後從服務上查詢消息隊列,如下:
有一個名爲test_queue的隊列,消息數量爲1。
消費者代碼:
public class Recv {
private final static String QUEUE_NAME = "test_queue";
public static void main(String[] argv) throws Exception {
// 獲取到連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 聲明隊列,要創建的隊列已經存在則不再創建
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 定義隊列的消費者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 監聽隊列
channel.basicConsume(QUEUE_NAME, true, consumer);
// 獲取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message +"'");
}
}
}
work模式:
P:生產者
C1、C2:消費者
紅色:消息隊列
- 一條消息只能被一個消費者消費
生產者代碼:
public class Send {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] argv) throws Exception {
// 獲取到連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 聲明隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//發送50條消息
for (int i = 0; i < 50; i++) {
// 消息內容
String message = "" + i;
channel.basicPublish("", QUEUE_NAME, null,message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
Thread.sleep(i * 10);
}
channel.close();
connection.close();
}
}
消費者1:獲取一條消息休眠10ms後,返回確認狀態
public class Recv {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] argv) throws Exception {
// 獲取到連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 聲明隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 同一時刻服務器只會發一條消息給消費者
//channel.basicQos(1);
// 定義隊列的消費者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 監聽隊列,手動返回完成
channel.basicConsume(QUEUE_NAME, false, consumer);
// 獲取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message +"'");
//休眠
Thread.sleep(10);
// 返回確認狀態
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
消費者2:獲取一條消息休眠1000ms後,返回確認狀態
public class Recv2 {
private final static String QUEUE_NAME = "test_queue_work";
public static void main(String[] argv) throws Exception {
// 獲取到連接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 聲明隊列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 同一時刻服務器只會發一條消息給消費者
//channel.basicQos(1);
// 定義隊列的消費者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 監聽隊列,手動返回完成狀態
channel.basicConsume(QUEUE_NAME, false, consumer);
// 獲取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message +"'");
// 休眠1秒
Thread.sleep(1000);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
運行結果:
消費者1和消費者2獲得的消息數量相同
但是這樣是不合理的,消費者2獲得消息後休眠的時間長,所以它得到的消息數量應該少用消費者1。
work模式——能者多勞
將上面兩個消費者中的註解代碼 channel.basicQos(1) 解注,這就意味着同一時刻服務器只發一條消息給消費者,消費者返回確認狀態後獲得新的消息,這樣消費者2獲得的消息數量就少於消費者1.
消息確認模式:
自動確認:
消費者從隊列中獲取消息,無論消費者獲取到消息後是否成功消費,服務端都會認爲消息已經被成功消費。
如下代碼中,消費者不需要向服務器反饋
手動確認:
消費者從隊列獲得消息,服務器會將該消息標記爲不可用狀態,等待消費者反饋。如果消費者一直沒有反饋消息將一直處於不可用狀態。
手動模式消費者需要向服務端反饋狀態。
小結:
文章中的兩種MQ模式組成包括生產者、消費者、隊列,還有在代碼中體現出來的Connection連接、Channel通道。MQ的使用和hibernate、mysql的流程及其相像。首先獲得連接,通過連接獲得通道,通過通道完成消息隊列的聲明、消息的發佈,對消息的監聽等系列工作。