RabbitMQ总结

1. 什么是MQ(Message Queue)

用于服务之间进行异步通信的中间件。

2. 为什么要使用MQ

2.1 解耦

  1. 用户下单,访问订单系统,订单系统调用库存系统、物流系统。突然某天库存系统出现问题挂了,从而导致订单系统也跟着挂了,用户得到一个下单失败的反馈,系统容错性低。
  2. 某天产品经理找到订单系统的开发程序员,要添加一个X系统,只能修改订单系统代码,过两天又要添加一个Y系统,又要修改订单系统,订单系统的可维护性低。
  3. 使用了MQ,订单系统只需要将消息发送给MQ,返回用户下单成功。其他需要消息的系统只需要从MQ中取出消费即可。

提升了系统的容错性和可维护性。

2.2 异步

  1. 用户下单,订单系统向数据库写入耗时20ms,还需要访问库存、物流系统,耗时400ms、400ms,下单共耗时:20+400+400=820ms,太慢了。一般来说,请求在200ms以内对用户来说才是无感知的。
  2. 使用了MQ,订单系统只需要向MQ中发送消息,耗时5ms,此时下单共耗时:20+5=25ms,此时用户体验好到爆。

提升了用户体验和系统吞吐量。

2.3 削峰

  1. A系统每秒最大处理1000请求,某天搞活动,请求增加到每秒5000个,A系统瞬间宕机了。
  2. 使用了MQ,用户请求发到MQ中,A系统慢慢的从MQ中每秒拉取1000个请求处理消费,游刃有余。

提高了系统的稳定性。

3. MQ的缺点与解决

3.1 系统可用性降低

由于引入了MQ,还需要保证MQ的高可用,服务才不会崩溃,如何保证MQ饿高可用性

3.1.1 RabbitMQ高可用

RabbitMQ基于主从模式做高可用。

主从模式:又称之为主备模式。如果主节点(提供读写能力)挂了,就切换到备用节点(不提供读写能力),并将备用节点升级为主节点使用。

挂了
好了
主节点挂了
主节点好了
主节点
不可用
恢复主节点
副节点
变为主节点
恢复副节点
  1. 单机模式,不用
  2. 普通集群模式
    多个机器部署RabbitMQ,创建的queue只存在于其中一个节点,其它节点同步元数据,若存放queue的节点宕机了,则无法从其他节点拉取数据。
  3. 镜像集群模式(高可用)
    创建的queue存在每一个节点上,写消息到queue时,自动同步到所有的节点的queue上。

3.2 系统复杂度提高

由于引入了MQ进行异步调用,如何保证消息没有被重复消费?怎么处理消息丢失情况,怎么保证消息传递的顺序性

3.2.1 消息幂等性处理 (保证消息没有重复消费)

  1. 什么是消息幂等性?
    用户对于同一操作发起的一次或者多次请求的结果是一致的。
  2. 幂等性实现方案
    • 使用数据库乐观锁机制

    • 唯一ID+指纹码

      • 唯一ID在加上指纹码(可以由系统生成,或者指定某种机制生成)保证这次操作是唯一的。
      • 优势 实现简单,就一个拼接,查询是否重复即可。
      • 弊端 高并发下有单个数据库写入的性能瓶颈 。
      • 解决方案 根据ID进行分库分表算法路由。
        对 id 进行算法路由,落到一个具体的数据库,然后当这个 id 第二次来又会落到这个数据库,这时候就像单库时的查重一样了。利用算法路由把单库的幂等变成多库的幂等,分摊数据流量压力,提高性能。
    • 利用Redis原子性
      幂等性概念及业界主流解决方案.

3.2.2 处理消息丢失情况

  1. Producer弄丢数据
    • 事务机制(同步)
      吞吐量降低,耗性能
    • confirm确认模式(异步,推荐)
      生产者发送消息到Broker中,无论成功与否都会触发一个回调函数confirmCallback(),第一个参数中有一个不可变的唯一 id,第二个参数表示是否接受成功,第三个参数表示失败的原因。
/**
 * @param correlationData 相关配置信息
 * @param ack 成功为true,失败为false
 * @param cause 失败原因
 **/
confirm(CorrelationData correlationData, Booleean ack, String cause)
  1. RabbitMQ宕机
    开启持久化。
  2. exchange到queue投递失败
    • return退回模式
      投递失败时会触发一个回调函数returnCallback方法,需要设定mandatory=true,否则失败消息将不会返回。
/*
 3. @param message 消息对象
 4. @param reply 错误码
 5. @param replyText 错误信息
 6. @param exchange 交换机
 7. @param routingKey 路由key
 **/
returnedMessage(Message message, int replyCode, String replyText, 
						String exchange, String routingKey);
  1. Consumer Ack(MQ到Consumer失败)
    1. 自动确认(默认)
      设置acknowledge=“none”。
      消息一旦被Consumer接收到,则自动确认收到,并将相应的message移除。很有可能消息接收到,在业务处理出现异常,name消息将会丢失。

    2. 手动确认
      acknowledge=“manual”
      需要在业务处理成功后调用channel.basicAck(),手动签收,如果出现异常,调用channel.basicNack()方法,让其自动重新发送消息。

    3. 根据异常情况确认(不做了解)
      acknowledge=“auto”

3.2.3 如何保证消息的顺序性

如何保证消息的顺序性

3.3 一致性问题

  1. A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,数据就不一致了。
    消息可靠性保障

Producer将消息入库。发送消息到Q1,Consumer监听Q1接收消息,操作业务,入库。发送确认接收信息到Q2,回调检查服务监听Q2将消息入库。延迟消息发送到Q3,回调检查服务监听Q3将消息与数据库中的比对,一致什么都不敢。若没有,则调用Producer重新发送消息。极端情况下,Producer发送的消息与延迟消息都失败,定时检查服务将比对MDB与Pruducer的DB,若不一致,将Producer中多出的消息重新发送。

此时如何发送延迟消息又成了一个问题。

3.3.1 发送延迟消息

  1. 使用插件

rabbitmq-delayed-message-exchange

  1. 使用TTL + 死信队列(DLX)
    • 什么是TTL(Time To Live)?
      消息到达过期时间,还未被消费,会被自动清除。
    • 什么是死信队列(dead Letter Exchange)?
      当消息成为Dead Message后,可以被重新发送到另一个交换机,这个交换机就是DLX
    • 消息如何成为Dead Message(死信)?
      • 队列长度到达限制。
      • 存在消息过期,到达过期时间未被消费。
      • 消费者拒收消息,basicNack/basicReject,并且不把消息放入原目标队列(requeue = false)

4. RabbitMQ限流

RabbitMQ限流

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值