1. 介绍

今天在复习kafka的一些知识。然后又对消息分发的语义进行了一番思考,还与小伙伴好好讨论了一番。这里小做总结。

2. mq如何做exactly once

要满足exactly once需要同时满足:

  1. 发送消息阶段,不允许发送重复的消息。
  2. 消费消息阶段,不允许消费重复的消息。

这个是文章十分钟入门RocketMQ里面写到的。

2.1 发送者如何保证不发送重复消息

  1. 在mq系统中,不采用重发策略(前提网络是完美的。。否则会丢消息,不符合整个系统是exactly once)
  2. 业务本身确保不发送重复的消息

2.2 消费者如何保证不重复消费

这里的不重复消费,主要指处理后的结果是唯一的,不会被应用程序方重复使用。

  1. 消费者做幂等操作来避免重复消费产生重复的处理结果。
  2. 消费者还可以根据消息体中的唯一key来去重,保证处理后结果的唯一性。

3. kafka如何做exactly once

3.1 从consumer下手

从consumer下手,分两种情况。

  1. 一种是由于发送者没收到broker的ack导致重发相同内容,offset不同的消息。这种情况需要做内容的去重操作。
  2. 另外一种情况是由于消费消息、提交offset这两部没做成一个原子操作导致的重复消费(异常原因导致提交offset失败) ###3.1.1 broker的ack引起的消息重复 我们知道kafka为了保证消息可靠性,我们会引入acks=all,retries>0的配置来保障生产者面对网络异常或者broker节点异常的时候会重发数据。但是这个必然会造成某些情况下的broker端消息重复。重复的消息产生了不同的offset。这样对于消费者来说,但从kafka本身设计层面很能处理这种重复消息。

解决办法: 消费者先消费消息,这时候会重复消费,先做完去重操作,再扔给应用程序接收方。这样保证应用程序方使用的消息处理结果是唯一的。

3.1.2 kafka client提交offset失败导致的重复消费

解决方案:

  1. 传统方式:把消息处理和提交offset做成原子操作。offset提交失败,消息处理也要回滚。用二阶段提交。
  2. kafka推荐方式:消息处理结果和offset的存储放在一起存储。

3.2 从producer何broker入手(自己想的,未进行证实是否有效)

producer发送前可以引入一个分布式全局唯一ID。分布式一致性可以利用ZK这种复合CP特性的协调工具。每次发送消息的时候先申请一个全局id,然后把全局id跟消息一起发给服务器,服务器返回确认。如果生产者没收到确认就再发送刚才的数据和id。假设服务器是收到消息的,只是生产者没有收到ack导致重发,这样重复的消息由于全局ID是一样的,所以在broker端不会重复。

4. 总结

其实如何把整个系统设计成exactly once的,还挺复杂的。而且可以在系统的不同层次上采用不同的策略来达到目标。例如幂等操作,可以放在MQ系统本身的设计中来做,也可以交给消费方自己去做。