文章详情页 您现在的位置是:网站首页>文章详情

消息队列基础篇(上)

图片丢失 Jeyrce.Lu 发表于:2019年12月6日 19:05 分类:【服务器 2460次阅读

        消息队列是最古老的中间件之一,从系统之间有通信需求开始,就自然产生了消息队列。但是给消息队列下一个准确的定义却不太容易。我们知道,消息队列的主要功能就是收发消息,但是它的作用不仅仅只是解决应用之间的通信问题这么简单。

一、消息队列的作用

  1. 异步处理

            处理一个秒杀请求包含了很多步骤,例如:风险控制;库存锁定; 生成订单;短信通知;更新统计数据。

    如果没有任何优化,正常的处理流程是:App 将请求发送给网关,依次调用上述 5 个流程,然后将结果返回给 APP。对于对于这 5 个步骤来说,能否决定秒杀成功,实际上只有风险控制和库存锁定这 2 个步骤。只要用户的秒杀请求通过风险控制,并在服务端完成库存锁定,就可以给用户返回秒杀结果了,对于后续的生成订单、短信通知和更新统计数据等步骤,并不一定要在秒杀请求中处理完成。所以当服务端完成前面 2 个步骤,确定本次请求的秒杀结果后,就可以马上给用户返回响应,然后把请求的数据放入消息队列中,由消息队列异步地进行后续的操作。


  2. 流量控制

            秒杀开始后,当短时间内大量的秒杀请求到达网关时,不会直接冲击到后端的秒杀服务,而是先堆积在消息队列中,后端服务按照自己的最大处理能力,从消息队列中消费请求进行处理。对于超时的请求可以直接丢弃,APP 将超时无响应的请求处理为秒杀失败即可。运维人员还可以随时增加秒杀服务的实例数量进行水平扩容,而不用对系统的其他部分做任何更改。这种设计的优点是:能根据下游的处理能力自动调节流量,达到“削峰填谷”的作用。


  3. 服务解耦

        在很多场景下,上游系统需要为下游系统服务,可是如果下游系统经常变动,使得上游系统提供的接口也需要跟着变动时,这使得改动成本过大,对于很多核心业务来说是难以接受的。

        后来工程师想了一个办法,我把数据放到消息队列里面,你们谁要谁拿,别找我,改代码是不可能改的。

        这种方式借助于消息队列的发布订阅机制,系统A发布了一个数据到某一个队列里面,其他系统根据需要来订阅该队列即可获取到该数据,上游系统与下游系统解耦,下游不再依赖上游系统,而只和消息队列通信。


二、主题和队列

    目前我们能够见到的消息队列中间件有很多种,常用的有RabbitMQ、RocketMQ、 Kafka、ActiveMQ等等,但是总的来说就可以分为队列消息模型主题消息模型

    1. 队列

    队列是一个数据结构,具有先进先出的特点,消息队列即队列里面存放的是消息。

    消息队列确实是这样设计的,生产者发送消息到队列里面,消费者从队列里面取数据,存取过程有序执行,这样的消息模型叫做队列消息模型。可是这样的模型却存在一些问题,当有多个消费者时,一个"消息"只可以分给一个消费者,每个消费者只能收到这个队列里面的一部分消息。

queue.jpeg

    2. 主题

    除了队列模型,常见的还有 发布-订阅模型,Kafka和RocketMQ就采用这样的消息模型。在发布 - 订阅模型中,消息的发送方称为发布者(Publisher),消息的接收方称为订阅者(Subscriber),服务端存放消息的容器称为主题(Topic)。发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”,订阅者可以收到其所订阅的主题内存放的所有消息。这俩模型区别没有特别大,主要区别在于一份消息传给多个消费者的实现方式

topic.jpeg

三、消息可靠性

mq-transaction.png

    1. 消息回查机制

    无论是Kafka还是RabbitMQ,在生产消息与消费消息时都有一个请求确认机制,以保障消息因为网络或服务故障而丢失。生产者往消息队列发送消息时,消息队列成功接收到该消息会往生产者发送确认,生产者没有收到确认就会重新发送;消费者从消息队列取消息时,消费者消费后往消息队列发送确认,消息队列没有收到已消费的确认也会重新将这条消息发给消费者,直到收到这条消息的确认。


    2. 分布式事务

    我们的业务系统先开启一个消息队列事务,往消息队列里面发送一个 half消息,这个half消息也是一个完整的消息,只是对消费者不可见,只有消息队列事务提交后才可见,half消息发送成功后,开始往mysql里面存储数据,如果往mysql里面存储失败了,也没有关系,MQ里面的half消息也不会被消费者拿去消费,成功了,我们就提交消息队列的事务,此时mysql有数据,消息队列里面有数据,这就保证了我们的消息是不会丢失的。但是如果提交事务的时候失败了,那么,此时mysql里面有数据,消息队列里面是half消息,这怎么办?kafka与RocketMQ给出了两种不同的解决方案

  • kafka:抛出异常

  • RocketMQ:事务回检

    kafka抛出一个特定的异常,需要我们的业务系统去捕获这个异常,判断mysql里面有没有这个消息对应的数据,如果有,我们可以尝试反复提交直到成功。


    那是不是通过事务与确认重传就可以保证消息不丢失呢?是的,一条消息从产生到消费,主要分为3个阶段:

  • 生产阶段: 在这个阶段,从消息在 生产者 创建出来,经过网络传输发送到消息队列。

  • 存储阶段: 在这个阶段,消息在 消息队列存储,如果是集群,消息会在这个阶段被复制到其他的副本上。

  • 消费阶段: 在这个阶段,消费者 从 消息队列 上拉取消息,经过网络传输发送到 消费者 。

版权声明 本文属于本站  原创作品,文章版权归本站及作者所有,请尊重作者的创作成果,转载、引用自觉附上本文永久地址: http://blog.lujianxin.com/x/art/1p8vf0spd30r

文章评论区

作者名片

图片丢失
  • 作者昵称:Jeyrce.Lu
  • 原创文章:61篇
  • 转载文章:3篇
  • 加入本站:1836天

站点信息

  • 运行天数:1837天
  • 累计访问:164169人次
  • 今日访问:0人次
  • 原创文章:69篇
  • 转载文章:4篇
  • 微信公众号:第一时间获取更新信息