架構圖
當有多個消費者時,我們的訊息會被哪個消費者消費?我們又如何均衡消費者消費資訊的多少?
主要又兩種模式
1、輪詢模式的分發:一個消費者一條,按均分配
2、公平分發:根據消費者的消費能力進行公平分發,處理快的多處理,處理慢的少處理,按勞分配。
Work模式-輪詢模式(Round-Robin)
- 型別:無
- 特點:該模式接收訊息是當有多個消費者接入時,罅隙的分配模式是一個消費者一條,直至訊息消費完成。
生產者消費者程式碼
public class Producer {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.33.180");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("szy10086");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("生產者");
channel = connection.createChannel();
// 準備傳送訊息內容
for (int i = 0; i < 20; i++) {
String msg = "message:"+i;
channel.basicPublish("","queue1",null,msg.getBytes());
}
System.out.println("傳送訊息成功!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("訊息傳送出現異常");
} finally {
// 關閉通道釋放連線
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消費者 Work1 程式碼
public class Work1 {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.33.180");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("szy10086");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("消費者-Work1");
channel = connection.createChannel();
Channel finalChannel = channel;
// finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
try {
System.out.println("Work1-收到的訊息是:" + new String(message.getBody(), "UTF-8"));
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, new CancelCallback() {
public void handle(String consumerTag) throws IOException {
System.out.println("Work1-獲取訊息失敗");
}
});
System.out.println("Work1-開始接收訊息");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 關閉通道釋放連線
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消費者 Work2
public class Work2 {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.33.180");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("szy10086");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("消費者-Work2");
channel = connection.createChannel();
Channel finalChannel = channel;
// finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
try {
System.out.println("Work1-收到的訊息是:" + new String(message.getBody(), "UTF-8"));
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, new CancelCallback() {
public void handle(String consumerTag) throws IOException {
System.out.println("Work2-獲取訊息失敗");
}
});
System.out.println("Work2-開始接收訊息");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 關閉通道釋放連線
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
首先建立佇列 queue1,然後啟動 Work1 和 Work2 兩個消費者,接著啟動 Producer 生產者。可以看到如下效果
這就是輪詢分發模式,但是會出現問題,該模式不會因為實際情況的網路頻寬的延遲,伺服器資源等來進行合理分配
公平分發測試
消費者程式碼不變,Work1 和 Work2 消費者的程式碼變為手動應答機制,這裡以 Work1 程式碼為例
public class Work1 {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.33.180");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("szy10086");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("消費者-Work1");
channel = connection.createChannel();
final Channel finalChannel = channel;
// 定義指標,qos=1,預設是沒有設定,為null,所以預設為輪詢分發,1 表示每次從佇列中取多少條資料
finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", false, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
try {
System.out.println("Work1-收到的訊息是:" + new String(message.getBody(), "UTF-8"));
Thread.sleep(800);
finalChannel.basicAck(message.getEnvelope().getDeliveryTag(),false);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, new CancelCallback() {
public void handle(String consumerTag) throws IOException {
System.out.println("Work1-獲取訊息失敗");
}
});
System.out.println("Work1-開始接收訊息");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 關閉通道釋放連線
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
程式碼執行效果如下,由於 Work2 執行效率快很多,所以 Wokr2 消費了大部分的訊息
本作品採用《CC 協議》,轉載必須註明作者和本文連結