默认状况下,消息发送端发送消息给 RabbitMQ 后,RabbitMQ 是不会返回任何信息的。 那么咱们怎么知道消息是中途丢失了仍是到达了 broker 呢?spring
RabbitMQ 提供了两种确认消息是否投递成功的方法bash
两种模式不能共存 服务器
RabbitMQ 中与事务机制有关的方法有三个网络
在经过 txSelect 开启事务以后,咱们即可以发布消息给broker代理服务器了,若是txCommit提交成功了,则消息必定到达了broker了,若是在 txCommit 执行以前 broker 异常崩溃或者因为其余缘由抛出异常,这个时候咱们即可以捕获异常经过 txRollback 回滚事务了框架
事务确实可以解决 producer 与 broker 之间消息确认的问题,只有消息成功被 broker 接收,事务提交才能成功,不然咱们即可以在捕获异常进行事务回滚操做,同时进行消息重发,可是使用事务机制的话会下降RabbitMQ的性能。异步
RabbitMQ 提供了一个更好的方案,使用 channel 信道的 confirm 模式。ide
生产者经过调用 channel 的 confirmSelect 方法将 channel 设置为 confirm 模式. 该模式下,全部在该信道上发布的消息都会被分派一个惟一的ID(从1开始),当消息被投递到全部匹配的队列后,broker 就会发送一个(包含消息的惟一 ID 的)确认给发送端, 若是 RabbitMQ 由于自身内部错误致使消息丢失,就会发送一条nack消息,发送端的 Confirm Listener 会去监听应答post
broker回传给发送端的确认消息中 deliver-tag 域包含了确认消息的ID,此外 broker 也能够设置 basic.ack 的 multiple 域,表示到这个ID以前的全部消息都已经获得了处理性能
confirm模式最大的好处在于他是异步的,生产者能够在等信道返回的同时继续发送下一条消息。spa
上面说到“当消息被投递到全部匹配的队列后,broker 就会发送一个(包含消息的惟一 ID 的)确认给发送端”,万一发送确认后, rabbitMq 崩溃了,消息队列中的消息就都没了,这时候发送端还觉得消息还在队列中。
为了防止这种状况的发送,rabbitMq 须要对队列和消息进行持久化。
当消息和队列开启持久化以后,确认信息会等到消息写入磁盘以后再发出
// 添加一个确认监听
channel.addConfirmListener(new ConfirmListener() {
//消息失败处理
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
//deliveryTag;惟一消息标签
//multiple:是否批量
System.err.println("-------no ack!-----------");
}
//消息成功处理
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.err.println("-------ack!-----------");
}
});
复制代码
实际开发中不多会直接使用 RabbitMQ 包,更多时候是使用 spring 提供的 spring-rabbit ,它封装了 RabbitMQ 并将它集成到了 spring 框架中。
这里给出 springBoot 版的异步确认的详细实现,供你们参考。
批量 confirm 模式 和 异步confirm模式效率更高些,可是批量confirm模式下,若是 rabbitMq 返回失败,那么须要从新发送这一批消息,建议最好仍是选择异步confirm模式
RabbitMQ 的响应可能会超时,超时多是消息没有到达 mq ,也有多是网络延迟致使的。对于响应超时的消息,一般被认定为投递失败。
对于投递失败的消息,须要进行消息补偿
消息补偿由发送端本身设计,常见的设计方案为
数据落库,消息状态打标
如图
消息补偿机制可能会致使重复投递,重复投递可能致使消费端重复消费。但重复投递又没法彻底避免,所以消费端须要防止重复消费。
要保证 RabbitMQ 的消息可靠性投递,须要作到如下几点