《消息队列高手课》阅读笔记:Rabbit/Rocket/Kafka/模型/消息事务/保序等

说明

基础篇相当不错,只用 8 篇文章就把消息队列的特性勾勒了出来,让人具备了基本的选择和决策能力。进阶篇侧重讲解消息系统的实现,感兴趣的可以深入阅读。

李玥《消息队列高手课》

MQ使用场景

  1. 异步
  2. 流量控制:削峰填谷
  3. 服务解耦

秒杀系统

方案1: 网关接收到请求后,生成消息,后端异步处理完成后响应。这种方案会导致响应延迟,异步处理链条被拉长(从网关后就开始异步):

秒杀系统方案1

方案2:网关收到请求后,先去获取令牌,获取到令牌后在进行业务处理,未获取到令牌返回秒杀失败。这种方案更好,更早拦截了不需要处理的请求,异步流程缩短,系统实现难度降低:

秒杀系统方案2

RabbitMQ、RocketMQ、Kafka 对比

RabbitMQ 使用队列模型:

  1. producer –> exchange –> queue,用 exchange 模块实现消息的分发策略,譬如复制到多个队列
  2. 消息堆积会导致性能急剧下降

rabbitmq的队列模型

RocketMQ 使用使用发布-订阅模型 :

  1. 每个主题 topic 用多个队列的方式增加并发能力,只保证单个队列上消息是有序的
  2. 消费组之间隔离,在主题上分别记录各自的消费位置
  3. 消费组内部竞争,一个消费者已经消费的消息,不会被同组的其它消费者再次消费

rocketmq的主题模型

Kafka 和 RocketMQ 类似,只是把主题中的队列称为分区(Partition)。

事务消息

事务消息用于解决「消息队列与其它系统间的数据一致性问题」,譬如生成新订单时有两步操作:1. 写入订单数据库 2. 写入消息队列。理想情况是这两步操作同时成功/失败。 可以出现不一致的情况是:

  • 写入数据库,再写入消息队列时,出现异常导致失败,消息队列中的订单数据少于数据库,数据缺失

RocketMQ 和 Kafka 提供了事务消息功能:

  1. 开启事务,发送半消息 – 消息写入了队列但是未分发
  2. 执行对其它系统的操作,譬如在订单表中插入新订单
  3. 如果操作失败,回滚消息事务,消息不投递;如果操作陈工,提交消息事务,消息开始投递
  4. 如果提交/回滚时失败:kafka 直接抛出异常,如果是宕机结果未知;rocketMQ 定期检查未结束的事务,调用业务方提供的反查接口,决定提交/回滚事务。

事务消息

rocketmq 的事务反查机制:

rocketmq的事务反查机制

只看消息队列的状态,事务消息是一个完成事务,消息要么发出,要么未发出。但是如果和数据库和消息队列放到一起看待,这不是一个真正意义的事务,譬如消息未发出时,数据库中的记录已经可见,如果还有其它失败导致消息不能发出,数据库中的记录不会自动删除。

跨多个系统的事务很难实现。

消息传递保证标准

MQTT 给出了三种消息传递质量标准;

  1. At Most Once, 至多传递一次,可能不传递,即丢消息
  2. At Least Once: 至少传递一次,所有消息都被传递,保证不丢,但是一个消息可能重复传递多次
  3. Exactly Once: 恰好传递一次,所有消息都被会传递,保证不丢,并且每个消息只传递一次,没有重复

RocketMQ、Kafka、RabbitMQ 实现的都是 At Least Once,消费者需要自己处理消息重复的问题。

对于重复消息,最好的处理方式是实现操作幂等,即同一个消息处理重复处理多次结果不变。为消息分配全局唯一ID,然后通过记录消息ID的方式去重,这种方式不可取,因为至少需要引入两个新的系统,多个系统之间的需要协调处理的情况很多,譬如什么时候记录已经消费的消息的ID。

消息保序

消息保序:消息的消费顺序和生成顺序严格一致。

RocketMQ 和 Kafka 主题 topic 中的消息是不包序的,除非 topic 中只有一个队列。

在生成消息的时候,可以按照一定策略,把符合条件的消息写入同一个队列,实现局部保序。譬如按照用户ID哈希,把同一个用户的消息发送到一个队列上,队列的上的消费是保序的,所以这个用户的消息处理实现了保序。

参考

  1. 李佶澳的博客

推荐阅读

赞助商广告

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  李佶澳的博客  小鸟笔记  软件手册  编程手册  运营手册  爱马影视  网络课程  奇技淫巧  课程文档  精选文章  发现知识星球  百度搜索 谷歌搜索