消息队列核心面试点
如何保证消息不丢失
我们看到,一共三个阶段,生产消息,存储消息,消费消息,从三个阶段看消息如何不被丢失
- 生产消息
生产者发送消息至broker,需要处理broker的响应,无论是同步发送还是异步发送消息,
同步和异步都需要做好try catch,妥善的处理响应,如果broker返回写入失败等错误消息,需要重试发送
当多次发送失败需要报警、日志记录等
这样就能保证生产消息阶段消息不会被丢失 - 存储消息
存储消息需要在消息刷盘之后在给生产者响应,假设消息写入缓存就返回响应,如果此时机器突然断电消息就没了
而生产者以后消息发送成功了
如果broker是集群部署,有多副本机制,即消息不仅仅要写入当前broker,还要写入副本机器,
那配置成至少写入两台机器后在给生产者响应,这样就保证了存储的可靠了。 - 消费消息
有些同学认为消费者拿到消息后存入内存队列就直接返回给broker消费成功,这是不对的,
需要考虑到消息放到内存后消费者就宕机了怎么办,我们应该等消费者真正执行完业务逻辑之后在返回给broker消费成功
所以消费阶段只有真正处理完了业务逻辑后在给broker返回响应,那么消费阶段消息就不会丢失
- 小结:保证消息的可靠性需要三方配合
生产者:需要处理好broker的响应,出错情况下利用好重试、报警等手段
存储者:需要控制响应的时机,单机情况下消息刷盘后返回响应,集群多副本情况下,消息发送至两个副本及以上的情况下在返回响应
消费者:需要真正执行完业务逻辑后在返回响应给broker - 但是要注意的是,消息可靠性增强了性能就下降了,等待消息落盘、多副本同步后在返回响应都会影响性能
因此也要看业务,例如日志传输丢那么一两条关系不大就可以不用等待落盘在返回
如和处理重复消息
-
生产者发送重复消息
假如我们发送消息,就管发,不管broker的响应,那么我们发往broker是不会重复的
但是这样消息就完全不可靠了,我们的基本需求是消息至少要发送到broker上,那么就得等待broker的响应
假设broker已经写入消息了,生产者由于网络原因没有收到响应,又重新发送一次,此时消息就重复了 -
消费者重复消费消息
假设消费者拿到消息已经处理完了业务逻辑,也commit了,需要更新customer offset了,此时消费者宕机了
另一个消费者顶上,由于customer offset没有更新,于是又拿到了刚才那条消息,业务又被执行了一遍,消费消息重复了 -
正常业务而言消息重复是不可避免的,因此我们需要从另一个角度解决消息重复的问题,
关键点就是幂等,既然我们不能防止重复消息的产生,那么我们只能从业务方面来解决重复消息所带来的影响
幂等处理重复消息
幂等是数学上的概念,我们可以理解为同样的参数多次调用同一接口和一次调用产生的结果是一致的
例如这条sql: update t1 set money=150 where id=1 and money=100
执行多少次,money都等于150,这叫幂等
因此需要改善业务逻辑,在重复消费的情况下不会影响最终结果
比如订单,我们记录订单id作为key, 假如有重复的消息过来,先判断订单id是否处理过了,如果没有在进行下一步
幂等性解决重复消息的实际方法:
(1)数据库唯一约束,交易订单号和账户建立唯一索引,重复执行的时候会违反约束,所以只能成功执行一次
(2)redis的setnx: redis中有这个key就不能重复操作
(3)数据库查询:加分布式锁,查询有没有该订单号的流水,没有则可以插入
(4)全局id,生产者给每个数据增加一个全局id,消费者去查询该全局id有没有被消费过,没有则进行处理
如何保证消息有序性
有序性分全局有序和部分有序
- 全局有序
如果要保证全局有序只能有一个生产者往topic发送消息,并且一个topic内部只能有一个队列(分区),
消费者也必须是单线程消费这个队列,这样的消息就是全局有序的
我们一般不需要全局有序,即使同步mysql binlog也只需要保证单表有序即可
- 部分有序
因此绝大部分的需求是部分有序,部分有序我们可以将topic内部分成我们需要的队列数,
把消息通过特定的策略发往到固定的队列中,然后每个队列对应一个单线程处理的消费者,
这样即完成了部分有序的需求,又可以通过队列数量的并发提高消息处理效率
图中是多个生产者,一个生产者也可以,只要同类消息发往指定的队列即可
如何处理消息堆积
- 消息的堆积往往是因为生产者的生产速度与消费者的消费速度不匹配导致的,
有可能是因为消费者消息消费失败反复重试造成的,也有可能因为消费者消费能力弱,渐渐积压的 - 因此我们需要先定位消费慢的原因,如果是bug则处理bug,如果是因为消费本身能力弱,我们可以处理下消费逻辑
如果消费逻辑已经优化了,还是消费慢,可以考虑水平扩容,增加topic中的队列数量和消费者数量,
注意队列数一定要增加,不然新增加的消费者没有东西消费,一个topic中,一个队列只会分配给一个消费者
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)