美文网首页消息队列MQ
消息队列(三)kafka的一致性和失败处理策略

消息队列(三)kafka的一致性和失败处理策略

作者: joshuaXin | 来源:发表于2020-04-22 11:26 被阅读0次

    一:使用的kafka中可能碰到的一致性问题

    1.Producer的一致性

        如前篇所讲,假如对 登录系统,主流程是:用户请求-->验证账户密码-->返回成功;一些其他流程包括:积分、提醒、荣誉系统等;这里有个问题是:如果这两者有一个失败了怎么办?

    2.Brokers的一致性(不是重点,这个是由kafka自己保证的)

        由于Message由Producer发送到Topic的Master Partition的时候,为了保证可靠性,会等待In-Sync的Replicas都同步完成之后才会返回成功;这里同样有个问题:如果返回超时怎么办?

    3.Consumer的一致性

       在Consumer的客户端,为了提高效率,会分为Fetch线程和Consume线程,在consume完成之后,会提交offset;问题是:提交offset和consume仅有一个失败怎么办?

    二:kafka消费的三种语义

    1.At more once 至多一次,即消息可能会有丢失的情况

        这种语义,很难被接受,网络故障、主副本选举、超时等因素就可能造成这种现象,而且若没这方面的意识,日志可能都不会打印,人工补偿都没法做到,造成这种现象的原因有:

    a) 生产者:(很多参数可以影响,挑几个重点的)

          异步调用,并没有设置回调函数;

          ack=0,不等Broker确认就继续发送消息;

          ack=1,等待leader确认后再发送消息,若follower没有跟上, 且leader挂掉,再选举将丢失

          retries=0,不重试,或者重试之后,仍旧失败,不考虑重试队列或者人工补偿

    b) 消费者

        设置为自动提交,时间间隔设置的较短,且不手动提交offset;前文说到,Consumer分为Fetch和Consume两步,自动提交的offset是Fetch的,所以提交的最高offset的message还未处理;

    2.At lease once 至少一次,即消息可能会有重复的现象发生

        这种语义,被实现的较多,往往Consumer会拉到重复的message,再去在cache中做一层去重的处理,然后实现Exactly Once,至少在0.11之前未实现kafka事务之前;

    a) 生产者 

       ack=ALL/-1,即等待In-sync的follower确认,才继续发送;

      且retries > 1,且还是失败了之后,要有重试队列、死信队列、人工补偿的方案;

    b)消费者  :关闭自动提交,并每次手动提交offset

    3.Exactly once 正好一次,消息不多不少,需要一些外部条件辅助

       a) 在At least once 的基础上,在业务方做幂等或者去重,比如redis去重,或业务上幂等;

       b) 在At least once 的基础上,将offset和处理的结果组成一个原子事务,但这种方法,我个人觉得引入了更多的复杂性,参考 https://cloud.tencent.com/developer/article/1368989

       c) 上面保证了不出错的情况下的,出错的情况下,需要重试队列、死信队列进行补偿;

       d) kafka的0.11版本之后,提供了事务,利用事务可以实现;

    三:重试队列(解决Consumer失败,同时不阻塞消费,针对少量失败)

    1.实现原因

          kafka中没有实现重试队列和死信队列的功能,但是由于当前的message的offset如果不提交,就会阻塞后续的消费,所以需要预留失败的message补偿的机制;实现方法有几种:

    2.本地队列

           用本地队列去控制,设置在定时器中,给任务设置30s,5min,30min三次重试的机会,如果不行,持久化到DB中,进行人工干预,当然报警、日志都要跟上; 优点:逻辑简单、实现简单,缺点:需要一些机制保证本地cache的可靠性,比如加hook预防服务更新,但是这样仍不能完全解决,还要面临初始化重试队列、宕机来不及调用hook等问题;

    3.换RocketMQ、RabbitMQ,或者修改kafka实现

        这个代价略大,而且这种不太受我们控制,很多消息队列的选型是已经固定的;修改kafka实现也不可能;

    4.参考RocketMQ的思路,失败后直接放入新的topic:Retry+topic

        这种思路也面临很大的问题:就是kafka没有实现延时的功能,那么新的message可能瞬时就被消费了,但是这个时候导致失败的原因,比如DB连接、网络问题没有解决,很快尝试几次之后,就进到死信队列了;

        还有一种是《深入kafka》一书中也讲了一些方法,但是都需要改动kafka的内部实现,这个不太适合小型项目;

    5.失败后直接放入新的topic,Redis实现延时功能

       redis是很普遍的组件,而且对于少量失败、且对重试时间要求没那么严格的情况下,redis很适合,redis的持久化、哨兵之类的,对可靠性保证的还是很不错的;

    留一篇文章,留着回忆redis的时候,再写吧

    https://www.jianshu.com/p/3142f4092936

    相关文章

      网友评论

        本文标题:消息队列(三)kafka的一致性和失败处理策略

        本文链接:https://www.haomeiwen.com/subject/grnbihtx.html