MQ消息丢失
- 消息丢失可能出现在三个场景:生产者发送消息时,消息中间件丢失消息,消费者消息丢失
生产者消息发送消息丢失
产生的原因:消息发出后,网路问题,消息中间件没有收到信息
解决方案:
开启RabbitMQ的事务机制
try {
channel.txSelect();
// 发送消息
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, (msg).getBytes());
// int i =1/0;
channel.txCommit();
System.out.println("消息发送成功");
} catch (Exception e) {
channel.txRollback(); // 消息回滚
System.out.println("消息已经回滚");
}
开启Confirm模式
- Confirm模式有三种模式:普通确认模式,异步确认模式,批量确认模式
普通确认模式
- 普通Confirm,发送一条,确认一条
- 不足:效率较低
// 开启发送方确认模式
channel.confirmSelect();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
// 普通Confirm,发送一条,确认一条
if (channel.waitForConfirms()) {
System.out.println("消息发送成功" );
}
批量确认模式
- 批量发送信息
- 不足:批次发送的数量不确定,如果批量发送中有一条数据出现异常,所有消息要进行补偿重发
try {
channel.confirmSelect();
for (int i = 0; i < 5; i++) {
// 发送消息
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, (msg +"-"+ i).getBytes());
}
// 批量确认结果,ACK如果是Multiple=True,代表ACK里面的Delivery-Tag之前的消息都被确认了
// 比如5条消息可能只收到1个ACK,也可能收到2个(抓包才看得到)
// 直到所有信息都发布,只要有一个未被Broker确认就会IOException
channel.waitForConfirmsOrDie();
System.out.println("消息发送完毕,批量确认成功");
} catch (Exception e) {
// 发生异常,可能需要对所有消息进行重发
e.printStackTrace();
}
异步确认模式
// 用来维护未确认消息的deliveryTag
final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
// 这里不会打印所有响应的ACK;ACK可能有多个,有可能一次确认多条,也有可能一次确认一条
// 异步监听确认和未确认的消息
// 如果要重复运行,先停掉之前的生产者,清空队列
channel.addConfirmListener(new ConfirmListener() {
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("Broker未确认消息,标识:" + deliveryTag);
if (multiple) {
// headSet表示后面参数之前的所有元素,全部删除
confirmSet.headSet(deliveryTag + 1L).clear();
} else {
confirmSet.remove(deliveryTag);
}
// 这里添加重发的方法
}
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
// 如果true表示批量执行了deliveryTag这个值以前(小于deliveryTag的)的所有消息,如果为false的话表示单条确认
System.out.println(String.format("Broker已确认消息,标识:%d,多个消息:%b", deliveryTag, multiple));
if (multiple) {
// headSet表示后面参数之前的所有元素,全部删除
confirmSet.headSet(deliveryTag + 1L).clear();
} else {
// 只移除一个元素
confirmSet.remove(deliveryTag);
}
System.out.println("未确认的消息:"+confirmSet);
}
});
// 开启发送方确认模式
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
long nextSeqNo = channel.getNextPublishSeqNo();
// 发送消息
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, (msg +"-"+ i).getBytes());
confirmSet.add(nextSeqNo);
}
System.out.println("所有消息:"+confirmSet);
事务模式和Confirm模式的比较
- 性能方面:事务模式是同步进行,流程会阻塞,吞吐率会下降,比较损耗性能;confirm模式正好相反,异步执行,因此不会阻塞流程;
消息中间件发送消息过程中消息丢失
产生的原因:RabbitMQ接收到信息后出现故障,并且消息还没来得及发送给消费者,且没有进行持久化
解决方案:
@Bean("GpQueue")
public Queue GpQueue() {
// queueName, durable, exclusive, autoDelete, Properties
return new Queue("GP_TEST_QUEUE", true, false, false, new HashMap<>());
}
@Bean("GpExchange")
public DirectExchange exchange() {
// exchangeName, durable, exclusive, autoDelete, Properties
return new DirectExchange("GP_TEST_EXCHANGE", true, false, new HashMap<>());
}
消费者接收消息和处理消息过程中消息丢失
产生的原因:消费者信息处理失败的情况下,触发了自动ACK提交,出现了数据未消费成功却返回ack信息
解决方案: