事务消息
高可用及幂等
在分布式系统中一般有三种处理语义
-
at-least-once:至少一次,有可能会有多次。如果
producer 收到来自ack 的确认,则表示该消息已经写入到Kafka 了,此时刚好是一次,也就是我们后面的exactly-once 。但是如果producer 超时或收到错误,并且request.required.acks 配置的不是-1 ,则会重试发送消息,客户端会认为该消息未写入Kafka 。如果broker 在发送Ack 之前失败,但在消息成功写入Kafka 之后,这一次重试将会导致我们的消息会被写入两次,所以消息就不止一次地传递给最终consumer ,如果consumer 处理逻辑没有保证幂等的话就会得到不正确的结果。在这种语义中会出现乱序,也就是当第一次ack 失败准备重试的时候,但是第二消息已经发送过去了,这个时候会出现单分区中乱序的现象, 我们需要设置Prouducer 的参数max.in.flight.requests.per.connection ,flight.requests 是Producer 端用来保存发送请求且没有响应的队列,保证Producer 端未响应的请求个数为1 。 -
at-most-once:如果在
ack 超时或返回错误时producer 不重试,也就是我们讲request.required.acks=-1 ,则该消息可能最终没有写入kafka ,所以consumer 不会接收消息。 -
exactly-once:刚好一次,即使
producer 重试发送消息,消息也会保证最多一次地传递给consumer 。该语义是最理想的,也是最难实现的。在0.10 之前并不能保证exactly-once ,需要使用consumer 自带的幂等性保证。0.11.0 使用事务保证了。
如何实现exactly-once
要实现
单Producer 单Topic
每个
- 如果消息序号比
Broker 维护的序号大一以上,说明中间有数据尚未写入,也即乱序,此时Broker 拒绝该消息,Producer 抛出InvalidSequenceNumber 。 - 如果消息序号小于等于
Broker 维护的序号,说明该消息已被保存,即为重复消息,Broker 直接丢弃该消息,Producer 抛出DuplicateSequenceNumber - 如果消息序号刚好大一,就证明是合法的。
上面所说的解决了两个问题
- 当
Prouducer 发送了一条消息之后失败,broker 并没有保存,但是第二条消息却发送成功,造成了数据的乱序。 - 当
Producer 发送了一条消息之后,broker 保存成功,ack 回传失败,producer 再次投递重复的消息。
上面所说的都是在同一个
事务
在
- 实现
exactly-once 语义 - 保证操作的原子性,要么全部成功,要么全部失败。
- 有状态的操作的恢复
事务可以保证就算跨多个,在本次事务中的对消费队列的操作都当成原子性,要么全部成功,要么全部失败。并且,有状态的应用也可以保证重启后从断点处继续处理,也即事务恢复。在
为了实现这一点,