Kafka消息交付
Kafka消息交付可靠性保障以及精确处理一次语义的实现。
所谓的消息交付可靠性保障,是指Kafka对Producer和Consumer要处理的消息提供什么样的承诺。常见的承诺有以下三种:
最多一次(atmost once):消息可能会丢失,但绝不会被重复发送。
至少一次(at least once):消息不会丢失,但有可能被重复发送。
精确一次(exactly once):消息不会丢失,也不会被重复发送。
Kafka默认提供的交付可靠性保障是第二种,即至少一次。之前说过消息“已提交”的含义。即只有Broker成功“提交”消息且Producer接到Broker的应答才会认为该消息成功发送,如果没接到应答,则会重试,所以这会导致消息重复发送。
幂等性(Idempotence)和事务(Transaction)
无论是至少一次还是最多一次,都不如精确一次。即使Producer端重复发送了相同的消 息,Broker端也能做到自动去重。在下游Consumer看来,消息依然只有一条。
Kafka的精确一次是通过两种机制:幂等性(Idempotence)和事务(Transaction)。
幂等性Producer
Producer默认不是幂等性的,但我们可以创建幂等性Producer。仅需要设置一个参数即
// enable.idempotence被设置成true后,Producer自动升级成幂等性Producer
props.put(“enable.idempotence”, ture)
// 或者
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true)
幂等性Producer的作用范围:
它只能保证单分区上的幂等性,即一个幂等性Producer能够保证某个主题的一个分区上不出现重复消息,它无法实现多个分区的幂等性。其次,它只能实现单会话上的幂等性,不能实现跨会话的幂 等性。这里的会话,可以理解为Producer进程的一次运行。当重启了Producer进程之后,这种幂等性保证就丧失了。
如果想实现多分区以及多会话上的消息无重复,应该使用事务(transaction)或者依赖事务型Producer。这也是幂等性Producer和事务型Producer的最大区别!
事务
Kafka的事务概念类似于我们熟知的数据库提供的事务。Kafka对事务的支持,目前主要是在read committed隔离级别上做事情。它能保证多条消息原子性地写入到目标分区,同时也能保证Consumer只能看到事务成功提交的消息。
事务型Producer
事务型Producer能够保证将消息原子性地写入到多个分区中。这批消息要么全部写入成功,要么全部失败。另外,事务型Producer也不惧进程的重启。Producer重启回来后,Kafka依然保证它们发送消息的精确一次处理。
设置事务型Producer的方法也很简单,满足两个要求即可:
* 和幂等性Producer一样,开启enable.idempotence = true。
* 设置Producer端参数transactional.id。最好为其设置一个有意义的名字。
还需要在Producer代码中做一些调整
producer.initTransactions();
try {producer.beginTransaction();producer.send(record1);producer.send(record2);producer.commitTransaction();
} catch (KafkaException e) {producer.abortTransaction();
}
这段代码能够保证Record1和Record2被当作一个事务统一提交到Kafka,要么它们全部提交成功,要么全部写入失败。实际上即使写入失败,Kafka也会把它们写入到底层的日志中,也就是说Consumer还是会看到这些消息。
因此在Consumer端,读取事务型Producer发送的消息也是需要一些变更的。修改起来也很简单,设置isolation.level参数的值即可。当前这个参数有两个取值:
1. read_uncommitted:这是默认值,表明Consumer能够读取到Kafka写入的任何消息,不论事务型Producer提交事务还是终止事务,其写入的消息都可以读取。很显然,如果你用了事务型Producer,那么对应的Consumer就不要使用这个值。
2. read_committed:表明Consumer只会读取事务型Producer成功提交事务写入的消息。当然了,它也能看到非事务型Producer写入的所有消息。
总结:
幂等性Producer只能保证单分区、单会话上的消息幂等性;而 事务能够保证跨分区、跨会话间的幂等性。从交付语义上来看,自然是事务型Producer能做的更多。
但比起幂等性Producer,事务型Producer的性能要更差,在实际使用过程中,我们需要仔细评估引入事务的开销,切不可无脑地启用事务。
参考:11 | 无消息丢失配置怎么实现?-Kafka核心技术与实战-极客时间 (geekbang.org)