Kafka 的运行机制
当我们向某个 Topic 发送消息的时候,在 Kafka 的 Broker 上,会通过 Partition 分区的机制来实现消息的物理存储。一个 Topic 可以有多个 Partition,相当于把一个 Topic 里面的 N 个消息数据进行分片存储。消费端去消费消息的时候,会从指定的 Partition 中去获取。在同一个消费组中,一个消费者可以消费多个 Partition 中的数据。但是消费者的数量只能小于或者等于 Partition 分区数量。
![](https://i-blog.csdnimg.cn/blog_migrate/bef1980418a78ec11fd4b4552d35d667.png)
exactlyOnce
理解了 Kafka 的工作机制以后,再来理解一下 exactlyOnce 的意思,在 MQ 的消息投递的语义有三种:
- At Most Once: 消息投递至多一次,可能会丢但不会出现重复。
- At Least Once: 消息投递至少一次,可能会出现重复但不会丢。
- Exactly Once: 消息投递正好一次,不会出现重复也不会丢。
总结
目前市面上的 MQ 产品,基本上都没有提供 Exactly Once 语义的实现。 我们只能通过一些其他手段来达到 Exactly Once 的效果。也就是确保生产者只发送一次,消费端只接受一次。
- 生产者可以采用事物消息的方式,事务可以支持多分区的数据完整性,原子性。并且支持跨会话的 exactly once 处理语义,即使 producer 宕机重启,依旧能保证数据只处理一次开启事务首先需要开启幂等性,即设置 enable.idempotence 为true。然后对producer消息发送做事务控制。如果出现导致生产者重试的错误,同样的消息,仍由同样的生产者发送多次,这个消息只被写到 Kafka broker 的日志中一次
- 虽然生产者能保证在 Kafka broker 上只记录唯一一条消息,但是由于网络延迟的存在,有可能会导致 Broker 在投递消息给消费者的时候,触发重试导致投递多次。所以消费端,可以采用幂等性的机制来避免重试带来的重复消费问题。
其次,关于实现顺序消费问题。在 Kafka 里面,每个 Partition 分区的消息本身就是按照顺序存储的。所以只需要针对 Topic 设置一个 Partition,这样就保证了所有消息都写入到这一个Partition 中。而消费者这边只需要消费这个分区,就可以实现消息的顺序消费处理。