1、RabbitMQ官網工作(即work)模式的架構圖
2、pom文件需要引入有關MQ的2個依賴包
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.4.1</version>
</dependency>
3、MQ獲取連接工具類代碼如下:
package com.rf.rabiitmq.util;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @description: mq連接工具類
* @author: xiaozhi
* @create: 2020-04-21 09:03
*/
public class ConnectionUtil {
/**
* @Description: 獲取mq連接方法
* @Param: []
* @Author: xz
* @return: com.rabbitmq.client.Connection
* @Date: 2020/4/21 9:19
*/
public static Connection getConnection() throws Exception {
//定義連接工廠
ConnectionFactory connectionFactory=new ConnectionFactory();
//以下信息,在安裝mq後,登錄mq客戶端進行配置的信息
connectionFactory.setHost("localhost");//設置服務地址
connectionFactory.setPort(5672);//設置端口
connectionFactory.setVirtualHost("xzVirtualHosts");//設置虛擬主機名稱
connectionFactory.setUsername("xz");//設置用戶名
connectionFactory.setPassword("xz");//設置密碼
//通過連接工廠獲取連接
Connection connection = connectionFactory.newConnection();
return connection;
}
}
4、消息發送者,即服務端代碼如下:
package com.rf.rabiitmq.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rf.rabiitmq.util.ConnectionUtil;
/**
* @description: work模式 消息發佈者
* @author: xiaozhi
* @create: 2020-04-21 13:44
*/
public class WorkSend{
private final static String QUEUE_NAME="work_queue";//聲明隊列名稱
public static void main(String[] args) throws Exception{
//獲取mq連接
Connection connection = ConnectionUtil.getConnection();
//從連接中創建通道
Channel channel = connection.createChannel();
/**
* 聲明隊列
* @param1 隊列的名稱
* @param2 是否持久化 如果true(該隊列將在服務器重啓後繼續存在)
* @param3 是否排外 即只允許該channel訪問該隊列 一般等於true的話用於一個隊列只能有一個消費者來消費的場景
* @param4 如果我們聲明一個自動刪除隊列(服務器將在不再使用時刪除它)
* @param5 參數隊列的其他屬性(構造參數)
*/
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for(int i=0;i<10;i++){
//發佈消息
String message=i+"、後臺系統發佈消息,今天夜間能看到星星。";
/**
* @param1 交換機名稱
* @param2 隊列名稱
* @param3 其他屬性
* @param4 body消息體
* */
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
System.out.println(message);
Thread.sleep(i*10);
}
//關閉通道和連接
channel.close();
connection.close();
}
}
5、消息接收者,即客戶端1代碼如下:
package com.rf.rabiitmq.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.rf.rabiitmq.util.ConnectionUtil;
/**
* @description: work模式 客戶端1
* @author: xiaozhi
* @create: 2020-04-21 13:56
*/
public class WorkRecv1 {
private final static String QUEUE_NAME="work_queue";//聲明隊列名稱
public static void main(String[] args) throws Exception{
//獲取mq連接及通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
/**
* 聲明隊列
* @param1 隊列的名稱
* @param2 是否持久化 如果true(該隊列將在服務器重啓後繼續存在)
* @param3 是否排外 即只允許該channel訪問該隊列 一般等於true的話用於一個隊列只能有一個消費者來消費的場景
* @param4 如果我們聲明一個自動刪除隊列(服務器將在不再使用時刪除它)
* @param5 參數隊列的其他屬性(構造參數)
*/
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//同一時刻只能發送一條消息給消費者
channel.basicQos(1);
//定義隊列的消費者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
/**
* 監聽隊列 手動返回完成
* @param1 隊列名稱
* @param2 如果true,服務器應該考慮消息;如果是false,手動返回完成
* @param3 回調消費者對象的接口
* */
channel.basicConsume(QUEUE_NAME,false,queueingConsumer);
// 獲取消息
while(true){
//等待下一個消息傳遞並返回它
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
//檢索消息體
String message = new String(delivery.getBody());
System.out.println("客戶端1接收消息:"+message);
Thread.sleep(10);
// 返回確認狀態
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
}
6、消息接收者,即客戶端2代碼如下:
package com.rf.rabiitmq.work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.rf.rabiitmq.util.ConnectionUtil;
/**
* @description: work模式 客戶端2
* @author: xiaozhi
* @create: 2020-04-21 14:22
*/
public class WorkRecv2 {
private final static String QUEUE_NAME="work_queue";//聲明隊列名稱
public static void main(String[] args) throws Exception{
//獲取mq連接及通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
/**
* 聲明隊列
* @param1 隊列的名稱
* @param2 是否持久化 如果true(該隊列將在服務器重啓後繼續存在)
* @param3 是否排外 即只允許該channel訪問該隊列 一般等於true的話用於一個隊列只能有一個消費者來消費的場景
* @param4 如果我們聲明一個自動刪除隊列(服務器將在不再使用時刪除它)
* @param5 參數隊列的其他屬性(構造參數)
*/
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//同一時刻只能發送一條消息給消費者
channel.basicQos(1);
//定義隊列的消費者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
/**
* 監聽隊列 手動返回完成
* @param1 隊列名稱
* @param2 如果true,服務器應該考慮消息;如果是false,手動返回完成
* @param3 回調消費者對象的接口
* */
channel.basicConsume(QUEUE_NAME,false,queueingConsumer);
//獲取消息
while(true){
//等待下一個消息傳遞並返回它
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
//檢索消息體
String message = new String(delivery.getBody());
System.out.println("客戶端2接收到消息:"+message);
Thread.sleep(1000);
// 返回確認狀態
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
}
7、先啓動服務端代碼程序和客戶端1代碼程序,之後關閉服務端代碼程序和客戶端1代碼程序
- 原因:爲了先創建隊列
8、啓動客戶端代碼程序:
1)、啓動消息接收者,即客戶端1代碼程序。輸出如下圖:
2)、啓動消息接收者,即客戶端2代碼程序。輸出如下圖:
9、啓動服務端代碼程序
1)、啓動消息發送者,即服務端的代碼程序。控制檯輸出如下圖:
2)查看客戶端1即(消費者1)和客戶端2(即消費者2)的控制檯輸出如下:
10、結論
客戶端1和客戶端2接收到的消息條數不同。
11、結論的原因:
- 因爲客戶端中加入瞭如下代碼:
//同一時刻只能發送一條消息給消費者
channel.basicQos(1);
- 示例中客戶端1休眠10毫秒,而客戶端2休眠1秒,因此客戶端1接收到的數據多。