kafka消息交付语义的分析

一、producer和consumer三种消息交付的语义

在kafka中,在producer和consumer这两个维度上都有三种消息交付的语义:

  1. At most once ---- 消息可能会丢失但绝不重传.
  2. At least once ---- 消息可以重传但绝不丢失.
  3. Exactly once ---- 每一条消息只被传递一次.

二、producer三种消息交付的语义

在producer设置中,有一个选项为ack:每发送一次消息,都会要求broker返回一个消息回执。如果ack没有收到,且设置了重发次数,那么producer会进行重发。这个ack有三种模式:

// The level of acknowledgement reliability needed from the broker (defaults
// to WaitForLocal). Equivalent to the `request.required.acks` setting of the JVM producer.
// 等同于jvm kafka中的`request.required.acks` 

type RequiredAcks int16
const (
// 第一个模式,NoResponse doesn't send any response, the TCP ACK is all you get.
    NoResponse RequiredAcks = 0
// 第二个模式, WaitForLocal waits for only the local commit to succeed before responding.
    WaitForLocal RequiredAcks = 1
// 第三个模式,WaitForAll waits for all in-sync replicas to commit before responding.
// The minimum number of in-sync replicas is configured on the broker via the `min.insync.replicas` configuration key.
    WaitForAll RequiredAcks = -1
)


如果RequiredAcks设置为0,对于服务器是否收到请求,是没法保证的;并且参数retries(重发)也不会生效(因为客户端无法获得失败信息)。此时提供的是At most once的语义。

如果RequiredAcks大于0,producer在没有收到应答的情况下,会进行重发。此时提供的是At least once的语义。

三、对于producer,幂等性保证Exactly once

在kafka 0.11.0.0之前,是无法保证Exactly once的。但从0.11.0.0开始,producer引入了幂等性的概念,保证消息只会被传递一次。

那么kafka是如何实现的呢?用到了Producer ID(即PID)Sequence Number

PID:在初始化的时候,每个新的Producer会被分配一个唯一的PID。这个PID对用户是不可见的。 
Sequence Numbler:对于每个PID,该Producer发送数据的每个[ Topic, Partition ]都对应一个从0开始单调递增的Sequence Number

Broker端在缓存中保存了Sequence Numbler,对于接收的每条消息,如果其序号比Broker缓存中的序号大于1,则接受它;否则,将其丢弃,这样就可以防止消息重复提交。 
但是,以上只能保证单个Producer对于同一个[ Topic, Partition ]的Exactly Once语义,不保证同一个Producer一个topic下不同Partition的幂等。

四、对于producer,事务保证Exactly once

从0.11.0.0开始,kafka支持了producer事务。要注意的一点是,不要把“操作db的业务逻辑”跟“操作消息的业务逻辑”当成一个事务。因为操作DB数据库的数据源是DB,消息数据源是kfaka,是完全不同的两个数据;一种数据源(如mysql,kafka)对应一个事务,所以它们是两种独立的事务。kafka事务指“kafka一系列生产、消费消息等操作”组成一个事务。db事务是指“操作数据库的一系列增删改操作”组成一个事务。

对于producer和生产消息来说,如果是只有写(即一条消息要发送给多个topic),可以使用producer事务来保证“要么都发送了,要么就都没有发送”。

如果有消费消息,然后再发送给别的topic,最后提交offset,也可以使用producer事务来保证这一系列操作的原子性。比如消费者提交offset出现问题,导致consumer在重复消费消息的时候,生产者会重复生产消息给其他的消费者。

如果只是消费消息和提交offset,那么producer事务就显得没有意义了。因为这个和手动提交offset没有什么区别。

五、consumer三种消息交付的语义

Consumer 读取到消息之后,先进行offset提交,然后再处理消息。如果在某中间时刻消息处理失败了,那这条消息就再也不会被消费了。这对应于at-most-once的语义。

Consumer 读取到消息之后,先处理消息,最后再offset提交。采用这种方式,如果处理消息成功,但是在offset提交之前服务崩溃了,那么在服务重启之后,这条消息会再次被消费到。这对应于at-least-once的语义。

如果要Exactly once语义,则可以使用如下手段: 
消费处理失败指的是业务失败或者操作db失败。 
消费处理成功指的是业务成功或者操作db成功。 
1,如果消费处理失败,则需要额外记录此条消息的offset(对于有顺序要求的消费来说,此时还得停止消费),下次再统一去消费这些处理失败的offset的消息。 
2,同样消费处理失败,也可以利用producer事务来保证。例如,提交offset并且把offset发送到另一个topic中,来保证这一系列的原子性;如果消费处理失败了,则中断事务,offset就不会被发送到topic中,topic中保存的还是上次那个offset。 
3,如果消费处理成功,需要额外保存最新提交的offset到文件系统中,然后提交offset。这样,不管offset提交成功,还是失败,在重启之后,都可以从文件中拿到最新的offset。 
4,或者,消费处理成功的同时,把offset写到db中,意思就是:首先,consumer将offset值存储到相同的地方;然后,提交offset。这样,输出的地方保存的也是最新、最准确的offset。

尊重别人的劳动成果,原文:https://blog.csdn.net/jeffrey11223/article/details/80775080 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值