Kafka 幂等生产者和事务生产者特性(讨论基于 kafka-python | confluent-kafka 客户端)

2019/07/04 20:48
阅读数 74

Kafka 提供了一个消息交付可靠性保障以及精确处理一次语义的实现。通常来说消息队列都提供多种消息语义保证

最多一次 (at most once): 消息可能会丢失,但绝不会被重复发送。

至少一次 (at least once): 消息不会丢失,但有可能被重复发送。

精确一次 (exactly once): 消息不会丢失,也不会被重复发送。 

默认情况下社区维护的 python-kafka 包会使用 ack1 但是 retry 0 的设置,也就是说 python-kafka 不会对发送失败的消息进行重试。如果 partition leader 写入成功了就认为成功了。在极端情况下 leader 写入成功但是复制失败了,可能会导致消息丢失。但是这里如果发送都未经 leader 确认,那么程序会失败。如果没有重试会交给代码进行自动重试 如果有 retry 会自己重试来保证消息不丢失。还有一种情况时候 producer 发了 broker 也写成功了 在 broker 返回消息的时候消息没有到达,producer 又配置了重试,那么会进行重试,那么消息就重复了。

根据上述情况,所以 kafka-python 客户端设置的是至少一次的语义。

如果设置为 ack 为 0 ,那么实现的是 最多一次语义。

那么如何设置精确一次语义呢?

 

幂等 Producer

Kafka 提供了幂等生产者和事务生产特性来实现精确一次的语义。(很可惜截止到写文的今天,由社区维护的 kafka-python 也还没有实现幂等生产者和事务功能,但是 confluent 公司的 python 客户端已经在 1.0.0 版本提供了该功能。因为 confluent 公司依赖的 c kafka-client 已经实现了该功能。)

我们可以在 confluent-python 中设置

enable.idempotence=true

When set to true, the producer will ensure that messages are successfully produced exactly once and in the original produce order. The following configuration properties are adjusted automatically (if not modified by the user) when idempotence is enabled: max.in.flight.requests.per.connection=5(must be less than or equal to 5), retries=INT32_MAX (must be greater than 0), acks=allqueuing.strategy=fifo. Producer instantation will fail if user-supplied configuration is incompatible. 
Type: boolean

就可以开启生产者幂等模式。开启之后 broker 会帮我们做消息去重。具体的实现是 broker 端会通过一些字段来判断消息是否重复,并且会将这些重复消息丢弃掉。我并没有看过这一块 broker 的实现原理,所以感觉实现应该会比我说的复杂。功能上可以理解成这样即可。这个功能虽然看起来很叼,但是他只能保证单个分区的幂等性。也就是说只有单个分区是能保证消息去重的,如果发到其他分区上将不能保证是幂等。另外这个特性也无法跨会话。如果 producer 重启,重新与 broker 建立会话,该特性也会失效。可能有人会说我擦,单个 partitions 去重有啥用?仔细想想 partitions 的实现,在不考虑重发的情况下如果我们基于 key 进行分区,那么同样的 key 消息一定会去到同样的 partitions 那么就达到了幂等的效果。

 

事务 Producer 

事务 Producer 可以保证将消息原子性的写到多个分区中,要么全部写成功要么全部写失败。

很遗憾截止到目前为止 python 没有任何一个 client 实现了该特性 但是 confluent-python 承诺在 19年Q2-Q3 实现该功能

只能看下 java 客户端实现了。

设置 enable.idempotence=true
transactional.id 设置为一个值

此外生产者需要调整代码进行打包提交

producer.initTransactions();
try {
            producer.beginTransaction();
            producer.send(record1);
            producer.send(record2);
            producer.commitTransaction();
} catch (KafkaException e) {
            producer.abortTransaction();
}

这里可以从名字看出来就是类似 MySQL 的 开启事务 发送事务 提交事务 终止事务

kafka 提供了全部成功 or 全部失败的事务保证。这是怎么实现的 我还蛮好奇。。。回头查阅一下资料看看。

 

 

Reference:

https://github.com/confluentinc/confluent-kafka-python/releases  Confluent's Python client For Apache Kafka

https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md  librdkafka config

https://github.com/confluentinc/confluent-kafka-python/issues/357  Does python binding support exactly once semantics?

https://docs.confluent.io/current/clients/confluent-kafka-python/index.html  confluent-kafka doc

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部