MQ消息中间件,面试能问些什么?( 四 )


写数据的时候 , 生产者就写leader , 然后leader将数据落地写本地磁盘 , 接着其他follower自己主动从leader来pull数据 。 一旦所有follower同步好数据了 , 就会发送ack给leader , leader收到所有follower的ack之后 , 就会返回写成功的消息给生产者 。 (当然 , 这只是其中一种模式 , 还可以适当调整这个行为)
消费的时候 , 只会从leader去读 , 但是只有一个消息已经被所有follower都同步成功返回ack的时候 , 这个消息才会被消费者读到 。
实际上这块机制 , 讲深了 , 是可以非常之深入的 , 但是我还是回到我们这个课程的主题和定位 , 聚焦面试 , 至少你听到这里大致明白了kafka是如何保证高可用机制的了 , 对吧?不至于一无所知 , 现场还能给面试官画画图 。 要遇上面试官确实是kafka高手 , 深挖了问 , 那你只能说不好意思 , 太深入的你没研究过 。
但是大家一定要明白 , 这个事情是要权衡的 , 你现在是要快速突击常见面试题体系 , 而不是要深入学习kafka , 要深入学习kafka , 你是没那么多时间的 。 你只能确保 , 你之前也许压根儿不知道这块 , 但是现在你知道了 , 面试被问到 , 你大概可以说一说 。 然后很多其他的候选人 , 也许还不如你 , 没看过这个 , 被问到了压根儿答不出来 , 相比之下 , 你还能说点出来 , 大概就是这个意思了 。
MQ消息中间件,面试能问些什么?文章插图
2. 如何保证消息不被重复消费(如何保证消息消费时的幂等性)?其实这个很常见的一个问题 , 这俩问题基本可以连起来问 。 既然是消费消息 , 那肯定要考虑考虑会不会重复消费?能不能避免重复消费?或者重复消费了也别造成系统异常可以吗?这个是MQ领域的基本问题 , 其实本质上还是问你使用消息队列如何保证幂等性 , 这个是你架构里要考虑的一个问题 。
首先就是比如rabbitmq、rocketmq、kafka , 都有可能会出现消费重复消费的问题 , 正常 。 因为这问题通常不是mq自己保证的 , 是给你保证的 。 然后我们挑一个kafka来举个例子 , 说说怎么重复消费吧 。
kafka实际上有个offset的概念 , 就是每个消息写进去 , 都有一个offset , 代表他的序号 , 然后consumer消费了数据之后 , 每隔一段时间 , 会把自己消费过的消息的offset提交一下 , 代表我已经消费过了 , 下次我要是重启啥的 , 你就让我继续从上次消费到的offset来继续消费吧 。
但是凡事总有意外 , 比如我们之前生产经常遇到的 , 就是你有时候重启系统 , 看你怎么重启了 , 如果碰到点着急的 , 直接kill进程了 , 再重启 。 这会导致consumer有些消息处理了 , 但是没来得及提交offset , 尴尬了 。 重启之后 , 少数消息会再次消费一次 。
其实重复消费不可怕 , 可怕的是你没考虑到重复消费之后 , 怎么保证幂等性 。
给你举个例子吧 。 假设你有个系统 , 消费一条往数据库里插入一条 , 要是你一个消息重复两次 , 你不就插入了两条 , 这数据不就错了?但是你要是消费到第二次的时候 , 自己判断一下已经消费过了 , 直接扔了 , 不就保留了一条数据?
一条数据重复出现两次 , 数据库里就只有一条数据 , 这就保证了系统的幂等性
幂等性 , 我通俗点说 , 就一个数据 , 或者一个请求 , 给你重复来多次 , 你得确保对应的数据是不会改变的 , 不能出错 。
那所以第二个问题来了 , 怎么保证消息队列消费的幂等性?
其实还是得结合业务来思考 , 我这里给几个思路:
(1)比如你拿个数据要写库 , 你先根据主键查一下 , 如果这数据都有了 , 你就别插入了 , update一下好吧
(2)比如你是写redis , 那没问题了 , 反正每次都是set , 天然幂等性
(3)比如你不是上面两个场景 , 那做的稍微复杂一点 , 你需要让生产者发送每条数据的时候 , 里面加一个全局唯一的id , 类似订单id之类的东西 , 然后你这里消费到了之后 , 先根据这个id去比如redis里查一下 , 之前消费过吗?如果没有消费过 , 你就处理 , 然后这个id写redis 。 如果消费过了 , 那你就别处理了 , 保证别重复处理相同的消息即可 。
还有比如基于数据库的唯一键来保证重复数据不会重复插入多条 , 我们之前线上系统就有这个问题 , 就是拿到数据的时候 , 每次重启可能会有重复 , 因为kafka消费者还没来得及提交offset , 重复数据拿到了以后我们插入的时候 , 因为有唯一键约束了 , 所以重复数据只会插入报错 , 不会导致数据库中出现脏数据