RabbitMQ

一、什么是消息队列

  • 消息队列(Message Queue):是一种用于在应用程序之间传递消息的通信方式,消息队列允许应用程序异步地发送和接收消息,并且不需要直接连接到对方。
  • 消息(Message):是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。
  • 队列(Queue):一个数据结构,先进先出

二、消息队列的作用和应用场景

1. 应用解耦(系统之间解耦合)

  • 作用说明: 通过消息队列,发送者(生产者)只负责发送消息,不关心谁处理、如何处理,由接收者(消费者)负责消费消息,实现模块间的解耦。
    在这里插入图片描述

  • 应用场景举例:

    • 用户注册后需要发送欢迎邮件和短信 → 主系统将用户信息写入消息队列,邮件和短信系统分别监听队列处理。
    • 电商订单系统下单后需要通知库存系统 → 通过消息传递实现松耦合。

2. 异步提速(提升响应速度)

  • 作用说明: 对于一些耗时操作(如写日志、发送邮件、生成报表),不需要立即返回结果的,可以异步处理,显著提升接口响应速度和用户体验。在这里插入图片描述
  • 应用场景举例:
    • 用户支付成功后,页面立即跳转;后续的「发货单生成」、「积分奖励」等通过消息队列异步完成。
    • 新闻系统中用户评论后,后台异步更新热门榜单。

3. 削峰填谷(流量削峰)

  • 作用说明: 在高并发场景下,大量请求瞬间涌入会导致数据库或后端系统崩溃。通过消息队列“缓冲”请求,限制消费速度,从而保护后端系统。

在这里插入图片描述

  • 应用场景举例:
    • 秒杀系统:用户抢购请求写入队列,后台按顺序逐个处理,防止数据库被打爆。
    • 数据采集:采集服务将数据写入消息队列,分析系统平稳消费,防止高峰期宕机。

三、消息队列的缺点

1. 降低系统可用性

  • 描述: 系统引入了消息中间件(如 RabbitMQ、Kafka)作为新的外部依赖,一旦消息队列服务宕机,就会影响整个业务链路,导致系统不可用。

  • 举例:

    • 下单服务依赖 RabbitMQ 通知库存系统,如果 MQ 崩了,库存同步失败,导致超卖。
    • 依赖消息队列异步发送短信,MQ 不可用时用户收不到短信。
  • 解决建议:

    • 使用集群部署 RabbitMQ,开启镜像队列、持久化机制;
    • 在应用层做好降级处理,例如失败时记录日志或重试。

2. 系统复杂度提高

  • 描述: 使用 MQ 后,系统间通过消息传递协作,带来一系列新的挑战,例如:

    • 如何避免消息重复消费?
    • 如何处理消息丢失?
    • 如何保证消息顺序性?
    • 如何进行消息幂等处理?
  • 举例:

    • 支付结果通知被多次消费,重复发货;
    • 消息积压导致消费者延迟处理;
    • 顺序消费失败导致数据错乱。
  • 解决建议:

    • 为消费者设计幂等处理机制;
    • 配置消息确认机制(ACK/NACK);
    • 对顺序敏感的业务使用顺序队列事务消息

3. 数据一致性问题

  • 描述: 引入 MQ 后,业务变成了分布式事务场景,发送方可能已经提交成功,但部分消费方处理失败,导致系统间数据不一致

  • 举例:

    • 用户支付成功,支付系统成功返回,但消息通知发给 B、C、D 系统;
      • B、D 成功入库;
      • C 写库失败,造成系统间状态不一致。
  • 解决建议:

    • 使用事务消息(如 RocketMQ 支持)确保发送和消费的事务一致性;
    • 结合本地消息表 + 定时补偿任务机制;
    • 加入消费失败告警/人工处理机制。

四、RabbitMQ

1.四大核心:

  • 生产者
  • 消费者
  • 队列
  • 交换机

2.AMQP协议

  • AMQP协议是一种二进制协议,它定义了一组规则和标准,以确保消息可以在不同的应用程序和平台之间传递和解释,AMQP协议包含四个核心组件:
    • 消息
    • 交换机
    • 队列
    • 绑定

3.RabbitMQ工作原理

(1) RabbitMQ 工作原理概览

在这里插入图片描述

  [Producer]
      |
      | 发送消息
      v
  [Exchange 交换机]  ←→  路由规则
      |
      | 转发消息
      v
  [Queue 队列]
      |
      | 拉取/推送消息
      v
  [Consumer]

(2) 核心组成部分解释

组件作用
Producer(生产者)发送消息到 RabbitMQ
Exchange(交换机)接收生产者发来的消息,按照规则转发到一个或多个队列
Queue(队列)真正存储消息的地方,消费者从队列中读取消息
Consumer(消费者)从队列中消费消息,处理业务逻辑
Binding(绑定)把交换机和队列连接起来,指定路由规则

(3) 工作流程详解(6步走)

  1. 生产者发送消息
    生产者将消息发送给 Exchange,并指定一个 Routing Key(路由键)

  2. 交换机接收消息
    Exchange 不存储消息,只负责根据路由规则将消息转发给合适的队列。

  3. 路由匹配规则决定消息流向
    通过绑定关系(Binding),Routing Key 与队列匹配,决定消息进入哪个队列。

  4. 队列缓存消息
    队列中存储消息,等待消费者来消费。支持持久化设置(消息掉电不丢失)。

  5. 消费者连接并消费消息
    消费者从队列拉取或等待 RabbitMQ 推送消息,处理业务逻辑。

  6. 消息确认(ACK)机制
    消费者处理完后发送 ACK,RabbitMQ 才会删除消息;失败可以重新投递(NACK)或丢弃。


(4) Exchange 类型(4种)

类型描述示例
Direct精准路由匹配 Routing Key下单消息 → 订单队列
Fanout广播消息给所有绑定队列系统通知 → 所有服务
Topic模糊匹配通配符(如 a.*.b)日志级别 info/error
Headers根据消息头属性路由高级路由控制,较少用

(5) 生产者代码示例:

package com.rgg.rabbitmqhello;

import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * @author yexiebao
 * @date 2025/4/19
 */
public class Producer {
    public static void main(String[] args) throws Exception {
        //交换机名称
        String exchangeName = "exchange_name";
        //队列名称
        String queueName = "queue_name";
        //1.创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.RabbitMQ服务地址
        factory.setHost("192.168.2000.2");
        //3.账号
        factory.setUsername("admin");
        //4.密码
        factory.setPassword("123456");
        //5.端口号
        factory.setPort(5672 );
        //6.创建连接
        Connection connection = factory.newConnection();
        //7.创建信道
        Channel channel = connection.createChannel();

        /**
         * 创建交换机
         * 1.交换机名称
         * 2.交换机类型:direct,topic,fanout,headers
         * 3.指定交换机是否需要持久化,如果设置true,那么交换机的元数据要持久化
         * 4.指定交换机在没有队列绑定时,是否需要删除,设置false表示不删除
         * 5.Map<String, Object></String,>
         */
        channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,null);

        /**
         * 生成一个队列
         * 1.队列名称
         * 2.队列是否需要持久化,但注意只是队列名称这些元数据持久化,不是队列中消息的持久化
         * 3.表示队列是不是私有的,如果是私有的,只有创建他的应用程序才能消费消息
         * 4.队列在没有消费者订阅的情况下是否自动删除
         * 5.队列的一些结构化信息,比如声明死信队、磁盘队列
         */
        channel.queueDeclare(queueName,true, false,false,null);

        /**
         * 将我们的队列和交换机绑定
         * 1.队列名称
         * 2.交换机名称
         * 3.路由键,直连模式下可为队列名称
         */
        channel.queueBind(queueName,exchangeName,queueName);

        //发送消息
        String message = "hello rabbitmq";

        /**
         * 发送消息
         * 1.发送到哪个交换机
         * 2.队列名称
         * 3.其他参数信息
         * 4.发送消息的消息体
         */
        channel.basicPublish(exchangeName,queueName,null,message.getBytes());
        //关闭通道
        channel.close();
        //关闭连接
        connection.close();
    }
}

(6) 消费者代码示例:

package com.rgg.rabbitmqhello;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author yexiebao
 * @date 2025/4/21
 */
public class Consumer  {
    public static void main(String[] args) throws Exception {
        //交换机名称
        String exchangeName = "exchange_name";
        //队列名称
        String queueName = "queue_name";
        //1.创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.RabbitMQ服务地址
        factory.setHost("192.168.2000.2");
        //3.账号
        factory.setUsername("admin");
        //4.密码
        factory.setPassword("123456");
        //5.端口号
        factory.setPort(5672 );
        //6.创建连接
        Connection connection = factory.newConnection();
        //7.创建信道
        Channel channel = connection.createChannel();
        /**
         *    // 定义 DeliverCallback
         *         DeliverCallback deliverCallback = new DeliverCallback() {
         *             @Override
         *             public void handle(String consumerTag, com.rabbitmq.client.Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws Exception {
         *                 // consumerTag 是消费者的标签
         *                 // envelope 包含消息的元数据(如路由键、交换机名称等)
         *                 // properties 包含消息的属性(如内容类型、优先级等)
         *                 // body 是消息的实际内容(字节数组)
         *                 System.out.println("接收到消息: " + new String(body));
         *             }
         *         };
         */
        //接收消息的回调函数
        DeliverCallback deliverCallback = (consumerTage, message) ->{
            System.out.println("接收到消息"+new String(message.getBody()));
        };

        //取消消息的回调函数
        CancelCallback cancelCallback = consumerTage ->{
            System.out.println("消费消息被中断");
        };

        /**
         * 消费消息
         * 1.消费哪个队列
         * 2.消费成功之后是否需要自动应答,true:自动应答
         * 3.接收消息的回调函数
         * 4.取消消息的回调函数
         */
        channel.basicConsume("queue_name",true, deliverCallback, cancelCallback);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值