如何保证消息100%投递成功:
保证消息投递成功需要在生产者、消息队列、消费者3个环节控制
- 消息队列
消息队列需要将交换器、队列、消息持久化,防止因断电等问题导致数据因没有持久化而丢失 - 生产者
生产者需要确保消息发送到消息队列,需要使用事务或消息确认的方式可以确保数据发送成功,推荐使用消息确认方式。如果发送失败要安排重试
每个重要消息都应该在发送之前持久化到数据库,消费成功就更新消息状态,失败就安排重试。 - 消费者
默认情况下消费者在收到消息后会自动消息确认(ack),如果消费者在收到消息后程序异常,导致消息并没有成功消费。所以需要将自动确认改成手动确认,等成功消费消息后再确认。 - 补充
- 如果队列服务器断电、宕机怎么处理:
有可能消息没来得及持久化就断电,而且生产者此时没办法将消息发送到队列,所以消息发到消息队列之前持久化到数据库。使用服务定时获取消息状态,重新发送失败的消息
- 如果队列服务器断电、宕机怎么处理:
防止消息被重复消费:
可能出现重复消费的几种情况:
生产者发送了多条一样的消息
消费者成功处理消息后断网或宕机导致没有确认消息被消费,消息队列会再一次发送消息
解决重复消费的方案:
方案1:
使用唯一MessageID判断消息是否被消费过,比如创建订单消息,可以用订单ID做MessageID、支付成功使用流水号做MessageID
其他逻辑比如发货、退款、取消、备注等,可以加一个操作记录表,使用操作记录表ID做MessageID
消费端需要同样有一个消费记录表,在消费消息时先判断MessageID是否已存在,如果已存在就不插入。
也可以使用Redis,创建一个hash类型的缓存,订单号做key,MessageID做field,如果消费过,value为1
方案2:
使用Redis,与上面大致相同,消息消费前将MessageID作为key,通过redis防重,如果不存在就消费消息并添加redis缓存,如果key存在就丢弃消息
Unacked消息怎么处理:
未ack的消息状态会变为Unacked,客户端断开连接后,状态会变为Ready