幂等性

1. 简介

Kafka在 0.11 版本引入了一项重大特性,幂等性。所谓的幂等性就是指 Producer 不论向 Server 发送多少次重复数据,Server 端都只会持久化一条。
拿 http 举例来说,一次或多次请求,得到的响应是一致的(网络超时等问题除外),换句话说,就是执行多次操作与执行一次操作的影响是一样的。

如果,某个系统是不具备幂等性的,如果用户重复提交了某个表格,就可能会造成不良影响。例如:用户在浏览器上点击了多次提交订单按钮,会在后台生成多个一模一样的订单。

2. 生产者幂等性


对于Kafka来说,要解决的是生产者发送消息的幂等问题。在生产者生产消息时,如果出现 retry 时,有可能会一条消息被发送了多次,如果Kafka不具备幂等性的,就有可能会在partition中保存多条一模一样的消息。

为了实现生产者的幂等性,Kafka 引入了 Producer ID(PID)和 Sequence Number 的概念。

  • PID:每个 Producer 在初始化时,都会分配一个唯一的 PID,这个 PID 对用户来说,是透明的。
  • Sequence Number:针对每个生产者(对应 PID )发送到指定主题分区的消息都对应一个从 0 开始递增的 Sequence Number。


当 Producer 发送消息给 Broker 时,Broker 接收到消息并将其追加到消息流中。此时,Broker 返回 Ack 信号给 Producer 时,发生异常导致 Producer 接收 Ack 信号失败。对于 Producer 来说,会触发重试机制,将消息再次发送,但是,由于引入了幂等性,在每条消息中附带了 PID(Producer ID)和Sequence Number。相同的 PID 和 Sequence Number 发送给 Broker,而之前 Broker 缓存过之前发送的相同的消息,那么在消息流中的消息就只有一条,不会出现重复发送的情况。

  • 幂等性 Producer 只能保证单分区上的幂等性
    • 即只能保证某个主题上的一个分区上不出现重复消息,无法实现多个分区的幂等性
  • 幂等性 Producer 只能实现单会话上的幂等性,不能实现跨会话的幂等性
    • 会话:Producer 进程的一次运行,如果重启 Producer 进程,将丢失幂等性保证

配置幂等性:

1
props.put("enable.idempotence",true);

2. Exactly Once语义

将服务器的 ACK 级别设置为-1,可以保证 Producer 到 Server 之间不会丢失数据,即 At Least Once(至少一次) 语义。相对的,将服务器 ACK 级别设置为 0,可以保证生产者每条消息只会被发送一次,即At Most Once(最多一次) 语义
At Least Once 可以保证数据不丢失,但是不能保证数据不重复;相对的,At Least Once 可以保证数据不重复,但是不能保证数据不丢失。但是,对于一些非常重要的信息,比如说交易数据,下游数据消费者要求数据既不重复也不丢失,即 Exactly Once(刚好一次) 语义。在 0.11 版本以前的 Kafka,对此是无能为力的,只能保证数据不丢失,再在下游消费者对数据做全局去重。对于多个下游应用的情况,每个都需要单独做全局去重,这就对性能造成了很大影响。

0.11 版本的 Kafka,引入了一项重大特性:幂等性。幂等性结合 At Least Once 语义,就构成了 Kafka 的Exactly Once 语义。即:
At Least Once + 幂等性 = Exactly Once
Kafka 的幂等性实现其实就是将原来下游需要做的去重放在了数据上游。

事务

1. 简介

幂等性并不能跨多个分区运作,而事务可以弥补这个缺陷。Kafka 事务是 2017 年 Kafka 0.11 引入的新特性。类似于数据库的事务。Kafka 事务指的是在 Exactly Once 语义的基础上,生产和消费可以跨分区和会话,生产者生产消息以及消费者提交 offset 的操作可以在一个原子操作中,要么都成功,要么都失败。尤其是在生产者、消费者并存时,事务的保障尤其重要。(consumer-transform-producer模式)

2. Producer事务

为了实现跨分区跨会话的事务,需要引入一个全局唯一的 Transaction ID,并将 Producer 获得的 PID 和 Transaction ID 绑定。这样当 Producer 重启后就可以通过正在进行的Transaction ID 获得原来的PID。
为了管理 Transaction,Kafka 引入了一个新的组件 Transaction Coordinator。Producer 就是通过和 Transaction Coordinator 交互获得 Transaction ID 对应的任务状态。Transaction Coordinator 还负责将事务所有写入 Kafka 的一个内部 Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。

3. Consumer事务

上述事务机制主要是从 Producer 方面考虑,对于 Consumer 而言,事务的保证就会相对较弱,尤其是无法保证 Commit 的信息被精确消费。这是由于 Consumer 可以通过 offset 访问任意信息,而且不同的 Segment File 生命周期不同,同一事务的消息可能会出现重启后被删除的情况。