2.1.1 RabbitMq简要使用

1.消息队列概述

  分布式系统中用于解耦组件间通信的中间件技术。它通过异步处理的方式,使得生产者可以不受消费者的影响发送消息,而消费者则可以根据自己的节奏来处理这些消息。下面是RabbitMq得到内存结构图:

消息在消息队列中的数据通信流程

  1. 消息队列的结构分为三个部分,分别是生产者(Producer)、服务器(Server)、消费者(Consumer)。其中生产者和消费者通过Channel与服务器建立连接进行数据通信
  2. 服务器由交换机(Exchange)和队列(Queue)组成,生产者通过Channel与服务器建立连接,向服务器中的交换机发送消息服务器中的交换机接收生产者发送的消息,并根据生产者消息中的路由健将消息路由到指定的队列中。
  3. 消费者通过Channel与服务器建立连接根据队列名称找到服务器中队列再消费消息

2. 消息队列优点和问题

  消息队列在应用中使用较多,

1.优点

  1. 异步:允许生产者继续处理下一个任务,而无需等待消费者的响应,从而提高了系统的响应速度(接收请求到响应请求所需的时间,单个请求好坏)和吞吐量(单位时间内处理的请求或任务数量,RPS,TPS,整体系统性能)
  2. 服务解耦:生产者和消费者不需要直接通信,这使得系统更加灵活,易于扩展和维护。
  3. 流量削峰:在高负载期间,消息队列可以作为缓冲区存储消息,直到消费者能够处理它们,有助于平滑流量高峰。
  4. 可靠性保证:消息至少被消费一次或者恰好被消费一次,从而确保数据的一致性和完整性

2.问题定义以及发生原因

  • 消息重复消费:
  1. 定义:消息队列中的某一条消息被消费者多次消费;消费者在处理消息后未能正确确认消息已处理
  2. 原因网络故障网络不稳定,消息确认可能丢失);消费者崩溃消费者处理消息过程中崩溃,未能确认消息被处理);幂等性缺失消费者处理消息的逻辑不具备幂等性(即多次执行同一操作的效果等同于执行一次))。
  • 数据一致性:
  1. 定义:消息队列中消息的处理结果与预期状态保持一致
  2. 原因消息重复消费事务处理不当处理消息的同时更新数据库或其他外部系统时,如果没有正确使用事务,可能会导致数据不一致);错误处理不当消息处理过程中出现错误而没有妥善处理,可能会导致数据不一致)。
  • 消息丢失:
  1. 定义:消息队列中的消息未能被正确处理未能被消费者接收
  2. 原因消息队列故障消费者故障消费者在处理消息前崩溃未能正确接收消息);配置不当消息过期时间设置得太短,可能导致消息在被消费者接收之前就过期)。
  • 消息顺序:
  1. 定义消息队列中的消息按照一定的顺序被生产和消费
  2. 原因并发消费多个消费者并发处理消息可能导致消息顺序被打);分区消息(在分布式消息队列中,消息可能被分布在不同的分区中,导致顺序无法保证)。
  • 消息堆积:
  1. 定义:消息队列中积压了大量的未处理消息
  2. 原因消费者处理能力不足(消费者处理消息的速度慢于消息产生的速度);突发流量(在高峰时段,消息产生量剧增,超过了消费者的处理能力);系统资源限制(如 CPU、内存等资源不足,影响消息处理速度)。

3.解决方案

  • 消息重复消费:
    • 幂等性设计:确保消费者处理消息的逻辑具备幂等性,即无论消息被消费多少次,结果都是一致的
    • 消息确认机制:使用消息队列提供的消息确认机制,确保消息被正确处理后再确认。
    • 消息去重消费者端实现消息去重逻辑,例如使用消息 ID 或消息内容的哈希值作为标识来避免重复处理。
  • 数据一致性:
    • 事务消息:使用消息队列提供的事务消息功能确保消息的发送和业务操作作为一个完整的事务
    • 幂等性设计:确保消息处理逻辑具备幂等性
    • 错误处理机制:实现错误处理逻辑(代码发生异常时,如何处理,即在try{}catch(){}中定义异常后代码如何处理),确保消息处理过程中发生的错误能够被妥善处理。
  • 消息丢失:
    • 持久化:确保消息队列将消息持久化到磁盘防止服务重启时消息丢失
    • 死信队列:使用死信队列来捕获无法处理的消息确保消息不会丢失
    • 消息确认:确保消费者在消息处理完成后正确确认消息
    • 消息过期:合理设置消息的过期时间,避免过早过期
  • 消息顺序:
    • 有序消息队列:使用支持有序消息处理的消息队列
    • 单分区消费:确保消息在一个分区中被生产和消费,避免跨分区导致的顺序问题
    • 消息排序逻辑:在消费者端实现消息排序逻辑,确保消息按顺序处理。
  • 消息堆积:
    • 水平扩展增加消费者实例的数量,提高消息处理能力。
    • 消息优先级:有限处理重要消息。
    • 消息分组:将消息分为不同的组,由不同的消费者组处理减轻单一消费者组的压力

3.RabbitMq中某些问题的解决方案示例

   模仿一个简易的商品下单,向库存发去扣除库存,以及安排物流接件的场景,来实现RabbitMq解决上述部门问题(消息重复,消息丢失,消息顺序,数据一致性,消息堆积)

3.1 订单生产者

/**
 * 订单生产者:分别发送订单消息到库存和物流
 */
@Slf4j
@Service
public class OrderProducer {

    @Autowired
    private RabbitMqConfig rabbitMqConfig;

    public void sendMsg(){
        try {
            Channel channel = rabbitMqConfig.getChannel();
            channel.exchangeDeclare(RabbitMqConfig.ORDER_EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            //1.通过雪花算法保证订单id唯一,解决部门消息重复消费问题
            //TODO 暂时先写一个简单消息
            String orderId = "ORDER12345";
            String inventoryMsg = "{\"orderId\": \"" + orderId + "\", \"action\": \"reduce_stock\"}";
            String shippingMsg = "{\"orderId\": \"" + orderId + "\", \"action\": \"ship_order\"}";

            //2.消息发送确认机制,解决部分消息重复消费以及消息丢失问题
            //发送消息给库存
            channel.basicPublish(RabbitMqConfig.ORDER_EXCHANGE_NAME, RabbitMqConfig.ROUTING_KEY_INVENTORY_NAME, null, inventoryMsg.getBytes());
            //确认消息发送库存
            channel.waitForConfirms();

            //发送消息给物流
            channel.basicPublish(RabbitMqConfig.ORDER_EXCHANGE_NAME, RabbitMqConfig.ROUTING_KEY_SHIPPING_NAME, null, shippingMsg.getBytes());
            //确认消息发送库存
            channel.waitForConfirms();
        }catch (IOException e){
            log.error("消息队列发送消息IO异常", e.getMessage());
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("消息队列发送消息中断异常", e.getMessage());
        }
    }
}
生产者

3.2 库存消费者

 

3.3 物流消费者

 

posted @ 2024-08-16 15:12  求知律己  阅读(2)  评论(0编辑  收藏  举报