kafka消息投递语义

kafka支持3种消息投递语义

  • At most once:最多一次,消息可能会丢失,但不会重复(不确定消息是不是丢失,但是不再发送)
  • At least once:最少一次,消息不会丢失,可能会重复(不确定消息是不是丢失,但是还会再发送)
  • Exactly once:只且一次,消息不丢失不重复,只且消费一次(借助一些手段保证发送的消息是唯一)

但是整体的消息投递语义需要Producer端和Consumer端两者来保证。

Producer 消息生产者端

当producer向broker发送一条消息,这时网络出错了,producer无法得知broker是否接受到了这条消息。网络出错可能是发生在消息传递的过程中,也可能发生在broker已经接受到了消息,并返回ack给producer的过程中。

这时,producer会有两种选择:

  • 不管了(At most once),消息可能会丢失
  • 再发一次(At least once),消息可能会重复

想要实现Exactly once,需要给每个Producer在初始化的时候都会被分配一个唯一的PID,
Producer向指定的Topic的特定Partition发送的消息都携带一个sequence number(简称seqNum),从零开始的单调递增的。

Broker会将Topic-Partition对应的seqNum在内存中维护,每次接受到Producer的消息都会进行校验;
只有seqNum比上次提交的seqNum刚好大一,才被认为是合法的。比它大的,说明消息有丢失;比它小的,说明消息重复发送了。

通过设置ack的值,可以区分多种消息确认机制。producer端的acks设置如下:

  • acks=0 // 消息发了就发了,不等任何响应就认为消息发送成功
  • acks=1 // leader分片写消息成功就返回响应给producer
  • acks=all(-1) // 当acks=all, min.insync.replicas=2,就要求INSRNC列表中必须要有2个副本都写成功,才返回响应给producer,如果INSRNC中已同步副本数量不足2,就会报异常,如果没有2个副本写成功,也会报异常,消息就会认为没有写成功。

Broker 消息接收端

acks=1,表示当leader分片副本写消息成功就返回响应给producer,此时认为消息发送成功。

如果leader写成功但马上挂了,还没有将这个写成功的消息同步给其他的分片副本,那么这个分片此时的ISR列表为空

  • 如果unclean.leader.election.enable=true,就会发生log truncation(日志截取),同样会发生消息丢失。
  • 如果unclean.leader.election.enable=false,那么这个分片上的服务就不可用了,producer向这个分片发消息就会抛异常。

(unclean.leader.election.enable 是否允许不具备ISR资格的replicas选举为leader作为不得已的措施,甚至不惜牺牲部分数据。默认允许。建议允许。数据异常重要的情况例外。)

所以我们设置min.insync.replicas=2,unclean.leader.election.enable=false,producer端的acks=all,这样发送成功的消息就绝不会丢失。

Consumer 消息消费者端

所有分片的副本都有自己的log文件(保存消息)和相同的offset值。当consumer没挂的时候,offset直接保存在内存中,如果挂了,就会发生负载均衡,需要consumer group中另外的consumer来接管并继续消费。

consumer消费消息的方式有以下2种;

  • 读取消息后先保存offset,后处理消息(至少一次消费):保存offset成功,但是消息处理失败,这时来接管的consumer
    就只能从上次保存的offset继续消费,这种情况下就有可能丢消息,但是保证了at most once语义。

  • 读取消息后先处理消息,后保存offset(最多一次消费):消息处理成功,但是在保存offset时失败,这时来接管的consumer只能从上一次保存的offset开始消费,这时消息就会被重复消费,也就是保证了at least once语义。

© 2019 GuoYL's Notes All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero