消息中间件

RabbitMQ 在项目中的应用及相关问题

一、项目中 RabbitMQ 的使用场景

在我们的项目中,很多地方都使用了 RabbitMQ,它是服务通信的主要方式之一。项目中服务通信主要有两种方式实现:

  1. 通过 Feign 实现服务调用。
  2. 通过 MQ 实现服务通信。基本上除了查询请求之外,大部分的服务调用都采用 MQ 实现的异步调用。

二、选择 RabbitMQ 的原因及好处

(一)选择原因

RabbitMQ 功能丰富,支持各种消息收发模式(简单队列模式、工作队列模式、路由模式、直接模式、主题模式等),支持延迟队列、惰性队列,天然支持集群,保证服务的高可用,同时性能非常不错,社区也比较活跃,文档资料非常丰富。

(二)好处

  • **吞吐量提升:**无需等待订阅者处理完成,响应更快速。
  • **故障隔离:**服务没有直接调用,不存在级联失败问题。
  • 调用间没有阻塞,不会造成无效的资源占用。
  • 耦合度极低,每个服务都可以灵活插拔,可替换。
  • **流量削峰:**不管发布事件的流量波动多大,都由 Broker 接收,订阅者可以按照自己的速度去处理事件。

(三)缺点

  • 架构复杂了,业务没有明显的流程线,不好管理
  • 需要依赖于Broker的可靠、安全、性能

三、如何保证消息不丢失

消息从发送到消费者接收会经历多个过程,每一步都可能导致消息丢失。RabbitMQ 针对这些问题给出了解决方案:
在这里插入图片描述

(一)消息发送到交换机丢失

发布者确认机制(publisher-confirm),消息发送到交换机失败会向生产者返回 ACK,生产者通过回调接收发送结果,如果发送失败,重新发送,或者记录日志人工介入。

(二)消息从交换机路由到队列丢失

发布者回执机制(publisher-return),消息从交换机路由到队列失败会向生产者返回失败原因,生产者通过回调接收回调结果,如果发送失败,重新发送,或者记录日志人工介入。

(三)消息保存到队列中丢失

MQ 持久化(交换机持久化、队列持久化、消息持久化)。

(四)消费者消费消息丢失

消费者确认机制、消费者失败重试机制。

通过 RabbitMQ 本身所提供的机制基本上可以保证消息不丢失,但因为一些特殊原因还是会发生消息丢失问题,例如回调丢失、系统宕机、磁盘损坏等,这种概率很小。如果想规避这些问题,进一步提高消息发送的成功率:
在这里插入图片描述
可以设计一个消息状态表,主要包含消息 id、消息内容、交换机、消息路由 key、发送时间、签收状态等字段。发送方业务执行完毕后,向消息状态表保存一条消息记录,消息状态为未签收,之后再向 MQ 发送消息。消费方接收消息消费完毕后,向发送方发送一条签收消息,发送方接收到签收消息之后,修改消息状态表中的消息状态为已签收。之后通过定时任务扫描消息状态表中这些未签收的消息,重新发送消息,直到成功为止。对于已经完成消费的消息定时清理即可。

四、消息重复消费问题的解决方案

在使用 RabbitMQ 进行消息收发时,如果发送失败或者消费失败会自动进行重试,可能会导致消息的重复消费。解决方案如下:

(一)每条消息设置一个唯一的标识 id。

(二)幂等方案。

  1. token + redis。
  2. 分布式锁。
  3. 数据库锁(悲观锁、乐观锁)。

五、解决 100 万消息堆积的思路

解决消息堆积有三种思路:

(一)提高消费者的消费能力

  1. 使用多线程消费。
  2. 增加更多消费者,提高消费速度。
  3. 使用工作队列模式,设置多个消费者消费同一个队列中的消息。

(二)扩大队列容积,提高堆积上限

  1. 使用 RabbitMQ 惰性队列。
    • 惰性队列:接收到消息后直接存入磁盘而非内存,消费者要消费消息时才会从磁盘中读取并加载到内存,支持数百万条的消息存储。

六、保证消费顺序性的方法

一个队列只设置一个消费者消费即可,多个消费者之间无法保证消息消费顺序性。

七、RabbitMQ 延迟队列的实现方案

RabbitMQ 的延迟队列有两种实现方案:

(一)使用消息过期 TTL + 死信交换机。

(二)使用延迟交换机插件。

八、RabbitMQ 设置消息过期的方式

RabbitMQ 设置消息过期的方式有两种:

(一)为队列设置过期时间

所有进到这个队列的消息就会具有统一的过期时间。

(二)为消息单独设置过期时间

注意:队列过期和消息过期同时存在时,会以时间短的时间为准。

示例代码:

@Bean
public Queue ttlQueue(){
    return QueueBuilder.durable("ttl.queue") // 指定队列名称,并持久化
      .ttl(10000) // 设置队列的超时时间,10 秒
      .deadLetterExchange("dl.ttl.direct") // 指定死信交换机
      .build();
}

@Test
public void testTTLQueue() { 
    // 创建消息
    String message = "hello, ttl queue";
    // 消息 ID,需要封装到 CorrelationData 中
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); 
    // 发送消息
    rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
    // 记录日志
    log.debug("发送消息成功"); 
}

RabbitMQ 队列消息过期的机制是判断队列头部元素是否过期,如果队里头部消息没有到过期时间,中间消息到了过期时间,这个消息也不会被自动剔除。

九、消息成为死信的情况

当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):

(一)消费者使用 basic.reject 或 basic.nack 声明消费失败,并且消息的 requeue 参数设置为 false。

(二)消息是一个过期消息,超时无人消费。

(三)要投递的队列消息满了,无法投递。

十、死信交换机及队列绑定方法

死信交换机和正常的交换机没有什么不同,如果一个包含死信的队列配置了 dead-letter-exchange 属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机。
在这里插入图片描述
为队列绑定死信交换机,只需要设置队列属性 dead-letter-exchange 即可。

十一、RabbitMQ 的高可用机制

RabbitMQ 是基于 Erlang 语言编写,天然支持集群模式。RabbitMQ 的集群有两种模式:

(一)普通集群

是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。会在集群的各个节点间共享部分数据,包括交换机、队列元信息,但不包含队列中的消息。当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回。队列所在节点宕机,队列中的消息就会丢失。
在这里插入图片描述

(二)镜像集群

是一种主从集群,在普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。

  • 交换机、队列、队列中的消息会在各个 mq 的镜像节点之间同步备份。
  • 创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。
  • 一个队列的主节点可能是另一个队列的镜像节点。
  • 所有操作都是主节点完成,然后同步给镜像节点。
  • 主宕机后,镜像节点会替代成新的主节点。在这里插入图片描述

EMQ 在项目中的应用及相关问题

一、EMQ 简介及项目中的应用场景

  1. EMQ 是什么:EMQ X 是开源社区中最流行的 MQTT 消息服务器,广泛应用于物联网、移动互联网、智能硬件、车联网、电力能源等领域,如物联网 M2M 通信、Android 消息推送、移动即时消息、智能硬件、车联网通信等。
  2. 项目中的应用:在我们的项目中,主要使用 EMQ 实现服务器和物联网设备之间的信息传输,以及作为消息队列产品实现各个微服务之间的数据传输,例如设置状态实时监控、自动维修工单创建和自动补货工单创建、订单创建以及支付结果确认、设备出货控制、设置出货结果通知处理等。

二、使用 EMQ 保证消息不丢失的方法

在 MQTT 协议中规定了消息服务质量(Quality of Service),保证不同网络环境下消息传递的可靠性。MQTT 消息服务质量 QoS 等级有三个级别:

  1. 0:消息最多传递一次,可能会存在消息丢失。
  2. 1:消息至少传递一次,不会出现消息丢失,但可能会出现消息重复。
  3. 2:消息仅传递一次,不会出现消息丢失,也不会出现消息重复。

三、使用 EMQ 避免消息重复消费的方法

同样利用 MQTT 协议中的消息服务质量(Quality of Service)来避免消息重复消费。MQTT 消息服务质量 QoS 等级为 2 时,消息仅传递一次,不会出现消息丢失和重复消费的情况。

四、EMQ 支持延迟消息及实现方法

EMQ X 的延迟发布功能可以实现按照用户配置的时间间隔延迟发布 PUBLISH 报文的功能。当客户端使用特殊主题前缀 $delayed/{DelayInteval}发布消息到 EMQ X 时,将触发延迟发布功能。延迟发布的功能是针对消息发布者而言的,订阅方只需要按照正常的主题订阅即可。例如:

  • 前缀:randomstickyhash
  • 真实主题名:$queue/t/1
  • 描述:不带群组共享订阅时为$queue/t/1t/1;带群组共享订阅时为$share/abc,均衡策略可以是round_robin等,如$share/组名称/t/1。在所有订阅者中随机选择、按照订阅顺序轮询、按照发布者 ClientID 的哈希值、一直发往上次选取的订阅者等不同策略。

五、使用 EMQ 实现点对点消息和发布订阅消息的方法

默认情况下,EMQ 中的消息会发送给所有订阅了主题的订阅者,属于发布订阅机制。EMQ X 支持两种格式的共享订阅前缀:

模式示例前缀真实主题名
不带群组共享订阅Squeue/t/1Squeue/t/1
带群组共享订阅$share/组名称/t/1$share/abct/1

在 EMQ 中,如果想实现点对点消息,可以采用不带群组的共享订阅。通过这种方式,消息只会被订阅者列表中的某一个订阅者接收。
并且,可以在配置文件中配置负载均衡的策略,例如:

broker.shared_subscription_strategy = random

均衡策略描述
random在所有订阅者中随机选择
round_robin按照订阅顺序轮询
sticky一直发往上次选取的订阅者
hash按照发布者 ClientID 的哈希值

如果想不通的群组都只能有一个订阅者接收到消息, 可以使用带群组的共享订阅, 这样每个群组中都会有一个订阅者接收到消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

InnovatorX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值