对于重复消息,最好的方法是消息可重入(消息重复消费对业务无影响)。做不到可重入时,需要在消费端去重。

网络异常、服务器宕机等原因都有可能导致消息丢失。CMQ 为了做到不丢消息、可靠交付,采用了消息生产、消费确认机制。
**生产消息确认:**生产者向 CMQ 发送消息后,等待 CMQ 回复确认;CMQ 将消息持久化到磁盘后,向生产者返回确认成功。否则在生产者请求超时、CMQ 返回失败等情况下,生产者需要向 CMQ 重发消息。
**消费者确认:**CMQ 向消费者交付消息后,将消息置为不可见;在消息不可见时间内,消费者使用句柄删除消息。如果消息未被删除,且不可见时间超时,消息将重新可见。
由于消息确认机制是“至少一次交付(at least once)”,在网络抖动、生产者/消费者异常等情况下,就会出现生产者重复生产、消费者重复消费的情况。
要去重,先要识别重复消息。通常的做法是在生产消息时,业务方在消息体中插入去重 key,消费时通过该去重 key 来识别重复消息。去重 key 可以是由 <生产者 IP + 线程 ID + 时间戳 + 时间内递增值> 组成的唯一值。
只有一个消费者时,您可以将消费过的去重 key 缓存(如 KV 等),然后每次消费时检查去重 key 是否已消费过。去重 key 缓存可以根据消息最大有效时间来淘汰。CMQ 提供了队列当前最小未消费消息的时间(min_msg_time),您可以使用该时间和业务生产消息最大重试时间来确定缓存淘汰时间。 存在多个消费者时,去重 key 缓存就需要是分布式的。
根据消息最大有效时间,计算 key 过期时间点:
current_time + max_retention_time + max_retry_time + max_network_time (当前时间)+(最大有效时间)+(最大重试时间)+(最大网络时间)
根据 CMQ 最小未消费时间,计算 key 过期时间点:
min_msg_time + max_retry_time + max_network_time (最小未消费时间)+(最大重试时间)+(最大网络时间)
CMQ 可配置消息最大有效时间为15天,业务可根据实际情况调整。 CMQ 队列当前最小未消费消息时间,即下图中最远时间点。该时间之前的消息都已经被删除,之后的消息可能未被删除。