kafka生产者的幂等和事务处理

之前和大家聊过kafka是如何保证消息不丢失的,今天再讲讲在不丢消息的同时,如何实现精确一次处理的语义实现。

消息组件对消息的可靠性保障,常见的模式有3种:

  • 最多一次(at most once):消息可能会丢失,但不会重复
  • 至少一次(at least once):消息不会丢失,但有可能重复
  • 精确一次(exactly once):消息不会丢失,且不会重复,精准一次发送

kafka默认情况下,提供的是至少一次的可靠性保障。即broker保障已提交的消息的发送,但是遇上某些意外情况,如:网络抖动,超时等问题,导致Producer没有收到broker返回的数据ack,则Producer会继续重试发送消息,从而导致消息重复发送。
相应的,如果我们禁止Producer的失败重试发送功能,消息要么写入成功,要么写入失败,但绝不会重复发送。这样就是最多一次的消息保障模式。
但对于消息组件,排除特殊业务场景,我们追求的一定是精确一次的消息保障模式。kafka通过幂等性(Idempotence)和事务(Transaction)的机制,提供了这种精确的消息保障。

幂等

这里就不多说幂等的含义了,不清楚的自己查下资料。Producer默认不是幂等性的,向分区发送数据时,可能会出现同一条消息被发送多次导致消息重复的情况。但只需增加一些参数,即可开启幂等性。

1
2
3
props.put(“enable.idempotence”, ture)
或者
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true)

开启enable.idempotence后,kafka就会自动帮你做好消息去重的一系列工作。底层具体实现原理很简单,就是用空间换时间的优化思路,即在broker端多存一些字段来标识数据的唯一性。当Producer发送了具有相同字段值的消息后,broker会进行匹配去重,丢弃重复的数据。实际的代码没这么简单,但大致是这么个处理逻辑。
官方的这个幂等实现看似简单高效,但也存在他的局限性。他只能保证单分区上的幂等性,即一个幂等性Producer只能够保证某个topic的一个分区上不出现重复消息,无法实现多分区的幂等。此外,如果Producer重启,也会导致幂等重置。

事务

对于多分区保证幂等的场景,则需要事务特性来处理了。kafka的事务跟我们常见数据库事务概念差不多,也是提供经典的ACID,即原子性(Atomicity)、一致性 (Consistency)、隔离性 (Isolation) 和持久性 (Durability)。
事务Producer保证消息写入分区的原子性,即这批消息要么全部写入成功,要么全失败。此外,Producer重启回来后,kafka依然保证它们发送消息的精确一次处理。
事务特性的配置也很简单:

  • 和幂等Producer一样,开启enable.idempotence = true
  • 设置Producer端参数transctional.id

事务Producer的代码稍微也有点不一样,需要调一些事务处理的API。数据的发送需要放在beginTransactioncommitTransaction之间。Consumer端的代码也需要加上isolation.level参数,用以处理事务提交的数据。示例代码:

1
2
3
4
5
6
7
8
9
producer.initTransactions();
try {
producer.beginTransaction();
producer.send(record1);
producer.send(record2);
producer.commitTransaction();
} catch (KafkaException e) {
producer.abortTransaction();
}

事务Producer虽然在多分区的数据处理上保证了幂等,但是处理性能上相应的是会有一些下降的。

依赖redis实现幂等

这里为什么还要额外讲通过依赖redis来实现幂等呢?因为笔者在早期维护kafka相关应用时,那会0.8系列版本的kafka还没有这些自带的幂等事务特性,只能依靠开发者自己来实现。
常见的方式就是通过数据的业务属性来生成个uniqueId来维护到redis中,利用redis的高并发,高吞吐,分布式锁特性,让写入kafka多分区的数据前,先去redis中校验一下uniqueId等方式,来实现幂等。得益于redis的高性能,在保证幂等同时,还能不让消息数据吞吐性能下降太多。当然,因为redis的依赖引入,也增加了架构的复杂度,从运维上来说也增加了整体的故障点,其中取舍需要自己来全局判断。

这次大概先介绍了下kafka的幂等各种实现方式,实际在事务,和依赖redis分布式锁来实现幂等的方式中,还要许多点值得我们深究来聊一下的,篇幅所限,后续再细讲

hyperxu wechat
欢迎您扫一扫上面的二维码,订阅我的公众号!
坚持原创技术分享,您的支持将鼓励我继续创作!