概念
不公平分发可以理解为能者多劳,按照消费者的处理能力进行消息分配(处理快,消息分配多),以此提高队列消息处理速度,充分利用了消费者的性能。一般情况下会使用不公平分发,而不使用轮询。
-
// 消费方设置 prefetch = 1,即开启不公平分发
-
channel.basicQos(1);
预取值
可以为消费者指定分配固定任务数量,假设有七个任务,可以给A分配2个任务,给B分配4个任务。
// 消费者A:设置prefetch = 2
channel.basicQos(2);
// 消费者B:设置 prefetch=4
channel.basicQos(4);
发布确认
消费者能消费消息的前提是,提供者能正确推送消息到队列中。然而,如果在提供者往队列中推送消息时,RabbitMQ出现某些突发意外,比如重启,导致消息推送时丢失,这时就需要手动处理和恢复推送失败的消息。
三种发布确认方式:
- 单个确认
问题排查相对简单,但效率低。
- 批量确认
出现问题不易定位具体问题。执行效率高。
- 异步确认
发布消息的同时,采用异步确认方式提高执行效率。
实例代码:
单次发布确认
public static void producerSingleton() throws Exception {
Connection connection = RabbitConnectionUtil.getConnection();
// 队列声明
Channel channel = connection.createChannel();
channel.queueDeclare(queenName, true, false, false, new HashMap<>());
// 开启发布确认
channel.confirmSelect();
long startTime = System.currentTimeMillis();
for (int i = 0; i < SEND_COUNT; i++) {
String sendMessage = "第<" + i + ">消息 ";
channel.basicPublish("", queenName, null, sendMessage.getBytes(StandardCharsets.UTF_8));
// 单个消息马上进行发布确认
boolean flag = channel.waitForConfirms();
if (flag) {
System.out.println("消息发送成功!");
}
}
long endTime = System.currentTimeMillis();
System.out.println("单个发布确认:耗时:" + (endTime - startTime));
}
批量确认
public static void producerBatch() {
try {
Connection connection = RabbitConnectionUtil.getConnection();
// 队列声明
Channel channel = connection.createChannel();
channel.queueDeclare(queenName, true, false, false, new HashMap<>());
// 开启发布确认
channel.confirmSelect();
long startTime = System.currentTimeMillis();
// 50条确认一次
int confirmPoint = 50;
for (int i = 0; i < SEND_COUNT; i++) {
String sendMessage = "第<" + i + ">消息 ";
channel.basicPublish("", queenName, null, sendMessage.getBytes(StandardCharsets.UTF_8));
// 批量确认
if (i % confirmPoint == 0) {
channel.waitForConfirms();
// System.out.println("消息发送成功!");
}
}
long endTime = System.currentTimeMillis();
System.out.println("批量发布确认:耗时:" + (endTime - startTime));
} catch (IOException | TimeoutException | InterruptedException e) {
e.printStackTrace();
}
}
异步确认
public static void producerSync() {
try {
Connection connection = RabbitConnectionUtil.getConnection();
// 队列声明
Channel channel = connection.createChannel();
channel.queueDeclare(queenName, true, false, false, new HashMap<>());
// 开启发布确认
channel.confirmSelect();
long startTime = System.currentTimeMillis();
// 消息确认成功回调 (消息标记,是否批量确认)
ConfirmCallback ackCallback = (deliverTag, multiple) -> {
System.out.println("消息监听成功" + deliverTag);
};
// 消息确认失败回调 (消息标记,是否批量确认)
ConfirmCallback nackCallback = (deliverTag, multiple) -> {
System.out.println("监听失败 ==> " + deliverTag);
};
// 准备消息监听器,监听哪些消息状态
channel.addConfirmListener(ackCallback, nackCallback);
for (int i = 0; i < SEND_COUNT; i++) {
String sendMessage = "第<" + i + ">消息 ";
channel.basicPublish("", queenName, null, sendMessage.getBytes(StandardCharsets.UTF_8));
}
long endTime = System.currentTimeMillis();
System.out.println("批量发布确认:耗时:" + (endTime - startTime));
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
测试:
public static void main(String[] args) throws Exception {
// 1、单个确认: 可以快速定位确认失败位置,效率低
producerSingleton();
// 2、批量确认:不好定位确认位置,效率高
producerBatch();
// 3、异步批量确认: 兼容二者,效率高,可监听消息发送状态
producerSync();
}