消息队列

1.为什么要用消息队列(消息队列应用场景)

消息队列本质用的是队列,是一种“先进先出”的数据结构

常见应用场景:解耦、异步、削峰

解耦

在这里插入图片描述

系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。

在这里插入图片描述

使用消息队列解耦合,系统的耦合性就会提高了。比如物流系统发生故障,需要几分钟才能来修复,在这段时间内,物流系统要处理的数据被缓存到消息队列中,用户的下单操作正常完成。当物流系统恢复后,补充处理存在消息队列中的订单消息即可,终端系统感知不到物流系统发生过几分钟故障。

异步

在这里插入图片描述

A 系统接收一个请求,需要在自己本地写库,还需要在 B、C、D 三个系统写库,自己本地写库要 3ms,B、C、D 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户体验非常不好,一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的,如果用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。

在这里插入图片描述

如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,响应速度大大提升了,改善了用户的体验。

流量削峰

应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提升系统的稳定性和用户体验。

此外流量削峰在经济考量中还可以降低成本。

eg:业务系统正常时段的QPS如果是1000,流量最高峰是10000,为了应对流量高峰配置高性能的服务器显然不划算,这时可以使用消息队列对峰值流量削峰

2. 各种消息队列产品的比较

在这里插入图片描述

  1. ActiveMQ,早起使用的较多,没经过大规模吞吐量场景的验证,社区也不是很活跃,但是现在确实大家用的不多了,不推荐。
  2. RabbitMQ,开发语言 erlang 阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是RabbitMQ是开源的,比较稳定的支持,活跃度也高,如不考虑二次开发,追求性能和稳定性,推荐使用。
  3. RocketMQ,开发语言是Java,在阿里内部经受过高并发业务的考验,稳定性和性能均不错,考虑后期可能二次开发,推荐使用。
  4. Kafka ,大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,社区活跃度很高,推荐使用。大数据领域日志采集等业务推荐使用。

3. 消息队列的优点和缺点

优点:解耦、异步、削峰

缺点:

  1. 系统可用性降低

系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。

如何保证MQ的高可用?

  1. 系统复杂度提高

MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。

消息丢失怎么办? 重复消息怎么处理? 如何保证消息传递的顺序性?
3. 一致性问题

A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。

如何保证消息数据处理的一致性?

4. 如何保证消息队列的高可用

RabbitMQ-普通集群

在这里插入图片描述

  1. 在多台机器上分别启动RabbitMQ实例。
  2. 多个实例之间可以相互通信。
  3. 创建的Queue只会放在一个RabbitMQ上,其他实例都同步元数据。
  4. 消费的时候,如果连接的没有Queue,那么当前实例会从queue所在实例拉取数据。
    特点
  • 没有真正做到高可用
  • 有数据拉取的开销和单实例的瓶颈问题

RabbitMQ-镜像集群

在这里插入图片描述

  1. 在多台机器上分别启动RabbitMQ实例。
  2. 多个实例之间可以相互通信。
  3. 每次生产者写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。每个RabbitMQ节点上都有Queue的消息数据和元数据。
  4. 某一个节点宕机,其他节点依然保存完整数据,不影响客户端消费。

RocketMQ-双主双从

在这里插入图片描述

  1. 生产者通过Name Server发现Broker
  2. 生产者发送队列消息到2个Broker主节点
  3. Broker主节点分别和各自从节点同步数据
  4. 消费者从主或者从节点订阅消息

5. 如何保证消息不丢失

在这里插入图片描述

情况一:消息生产者没有成功发送到MQ Broker

情况二:消息发送给MQ Broker后,Broker宕机导致内存中的消息数据丢失

情况三:消费者获取到消息,但消费者还没有来得及处理宕机了,但此时MQ中消息已经删除,消费者重启后不能再消费之前的消息

解决方案

在这里插入图片描述

  1. 消息发送者发送给MQ Broker后,MQ Broker给生产者确认收到
  2. MQ收到消息后进行消息持久化
  3. 消费者收到消息处理完毕后手动进行ack确认
  4. MQ收到消费者ack确认后删除持久化的消息

6.如何保证消息不被重复消费(幂等性)

消息重复的根本原因是网络不可达。

发送时消息重复

在这里插入图片描述

当一条消息已被成功发送到服务端,此时出现了网络闪断,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同的消息。

消费时消息重复

在这里插入图片描述

消息消费的场景下,消息已投递到消费者并完成业务处理,当消费方给MQ服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,MQ服务端将在网络恢复后再次尝试投递之前已被消费方处理过的消息,此时消费者就会收到两条内容相同的消息。

解决方案

  1. 消息发送者发送消息时携带一个全局唯一的消息id
  2. 消费者获取消费后先根据id在redis/db中查询是否存在消费记录
  3. 如果没有消费过就正常消费,消费完毕后写入redis/db
  4. 如果消息消费过就直接舍弃

7. 如何保证消息消费的顺序性

消息有序指的是可以按照消息的发送顺序来消费。

例如:一笔订单产生了 3 条消息,分别是订单创建、订单付款、订单完成

消费时,要按照顺序依次消费才有意义。

与此同时多笔订单之间又是可以并行消费的

解决方案

1、全局顺序消息,生产者:MQ:消费者=1:1:1

2、局部顺序消息:
在这里插入图片描述

  1. 生产者根据消息ID将同一组消息发送到一个Queue中。
  2. 多个消费者同时获取Queue中的消息进行消费。
  3. MQ使用分段锁保证单个Queue中的有序消费。

RocketMQ发送方代码:

在这里插入图片描述

RocketMQ消费方代码:

在这里插入图片描述

8.基于MQ的分布式事务实现

在这里插入图片描述

  1. 用户提交订单。
  2. 库存服务操作库存DB,减库存。
  3. 订单服务操作订单DB,生成订单数据
  4. 库存服务和订单服务要么同时成功,要么同时失败。

本质上来说,分布式事务就是为了保证不同数据库的数据一致性。


消息发送方:

  1. 处理业务逻辑
  2. 保存消息到本地数据库
  3. 发送消息给MQ
  4. 监听MQ消息方通知消息,更改消息状态为已处理
  5. 定时任务将长期未处理消息重新发送到MQ

消息消费方:

  1. 监听MQ中间件消息
  2. 判断消息是否重复,重复就丢弃
  3. 消息未重复,执行本地业务
  4. 业务处理完毕,写消息记录到本地数据库
  5. 发送通知消息到MQ
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值