巧妙利用RabbitMQ常见交换机与队列的使用场景,解决异步通信、削峰填谷、广播通知、定时任务等问题

7 阅读24分钟

RabbitMQ全面实战:交换机、队列、场景落地与源码实现

在分布式系统中,消息中间件是实现异步通信、解耦服务、削峰填谷的核心组件。而 RabbitMQ 作为基于 AMQP(高级消息队列协议)的开源消息中间件,凭借其灵活的路由机制、可靠的消息投递、丰富的功能特性,成为企业级应用的首选。本文将从核心概念出发,详细拆解 RabbitMQ 的多种交换机、队列类型,结合实际业务场景提供解决方案,并附上可直接复用的 SpringBoot 整合源码,助力开发者快速上手并落地生产。

一、RabbitMQ 核心价值与核心概念

1.1 为什么选择 RabbitMQ?

  • 异步解耦:打破服务间同步依赖,比如订单系统无需等待支付系统响应即可完成下单,通过消息异步通知后续流程;
  • 削峰填谷:应对高并发场景(如秒杀、促销),将突发流量缓存到队列,消费者按能力平滑处理,避免服务雪崩;
  • 可靠投递:支持消息持久化、生产者确认、消费者 ACK、死信队列等机制,确保消息不丢失、不重复;
  • 灵活路由:通过多种交换机实现复杂路由规则,满足广播、精准匹配、模糊匹配等不同需求;
  • 多语言支持:兼容 Java、Python、Go 等多种语言,适配异构系统集成。

1.2 核心概念速通

在深入实战前,需先掌握 5 个核心概念,理解消息流转链路:

  • 生产者(Producer) :发送消息的应用程序;
  • 消费者(Consumer) :接收并处理消息的应用程序;
  • 交换机(Exchange) :接收生产者消息,根据路由规则将消息路由到绑定的队列(核心路由组件,不存储消息);
  • 队列(Queue) :存储消息的容器,与消费者绑定,按顺序投递消息;
  • 绑定(Binding) :将交换机与队列关联,同时指定「绑定键(Binding Key)」,交换机根据「路由键(Routing Key)」与绑定键的匹配规则路由消息;
  • 虚拟主机(Virtual Host) :隔离不同环境的资源(如开发、测试),每个虚拟主机有独立的交换机、队列、用户权限。

消息流转链路:生产者 → 交换机(按路由规则)→ 绑定的队列 → 消费者

二、RabbitMQ 四大核心交换机:特点、场景与源码

交换机是 RabbitMQ 路由机制的核心,不同交换机对应不同路由策略。以下详解四种常用交换机的使用场景与实战源码(基于 SpringBoot 2.7.x)。

2.1 Direct 交换机:精准路由(一对一匹配)

核心特点
  • 路由规则:消息的「路由键(Routing Key)」必须与队列的「绑定键(Binding Key)」完全一致,才会路由到对应队列;
  • 本质:一对一或多对一(多个队列绑定同一绑定键)的精准匹配。
适用场景
  • 单一目标路由:如订单支付成功后,仅通知订单系统更新状态;
  • 任务分发:将特定任务分配给指定消费者处理(如不同类型的任务路由到不同消费者队列)。
实战源码
1. 依赖配置(pom.xml)
<!-- RabbitMQ 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
2. 配置文件(application.yml)
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-confirm-type: correlated # 生产者确认
    publisher-returns: true # 消息返回回调
    listener:
      simple:
        acknowledge-mode: manual # 手动ACK
        concurrency: 3
        max-concurrency: 5
3. 交换机与队列配置
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DirectExchangeConfig {
    // 交换机名称
    public static final String DIRECT_EXCHANGE_NAME = "direct_order_exchange";
    // 订单状态更新队列
    public static final String ORDER_STATUS_QUEUE = "order_status_queue";
    // 支付通知队列
    public static final String PAY_NOTIFY_QUEUE = "pay_notify_queue";
    // 绑定键
    public static final String ORDER_STATUS_ROUTING_KEY = "order.status.update";
    public static final String PAY_NOTIFY_ROUTING_KEY = "pay.notify.send";

    // 1. 声明 Direct 交换机
    @Bean
    public DirectExchange directOrderExchange() {
        // durable: 持久化;autoDelete: 无绑定后自动删除;internal: 是否内部交换机(一般为false)
        return ExchangeBuilder.directExchange(DIRECT_EXCHANGE_NAME)
                .durable(true)
                .autoDelete(false)
                .internal(false)
                .build();
    }

    // 2. 声明队列
    @Bean
    public Queue orderStatusQueue() {
        // durable: 持久化;exclusive: 排他队列(仅当前连接可用);autoDelete: 无消费者后自动删除
        return QueueBuilder.durable(ORDER_STATUS_QUEUE)
                .exclusive(false)
                .autoDelete(false)
                .build();
    }

    @Bean
    public Queue payNotifyQueue() {
        return QueueBuilder.durable(PAY_NOTIFY_QUEUE)
                .exclusive(false)
                .autoDelete(false)
                .build();
    }

    // 3. 绑定交换机与队列(指定绑定键)
    @Bean
    public Binding orderStatusBinding() {
        return BindingBuilder.bind(orderStatusQueue())
                .to(directOrderExchange())
                .with(ORDER_STATUS_ROUTING_KEY);
    }

    @Bean
    public Binding payNotifyBinding() {
        return BindingBuilder.bind(payNotifyQueue())
                .to(directOrderExchange())
                .with(PAY_NOTIFY_ROUTING_KEY);
    }
}
4. 生产者(消息发送)
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class DirectProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送订单状态更新消息
     */
    public void sendOrderStatusMessage(Long orderId, String status) {
        String message = String.format("订单[%s]状态更新为:%s", orderId, status);
        // 发送消息:交换机名称 + 路由键 + 消息内容
        rabbitTemplate.convertAndSend(
                DirectExchangeConfig.DIRECT_EXCHANGE_NAME,
                DirectExchangeConfig.ORDER_STATUS_ROUTING_KEY,
                message,
                correlationData -> {
                    correlationData.setId(orderId.toString()); // 关联ID(用于生产者确认)
                    return correlationData;
                }
        );
        log.info("Direct交换机发送订单状态消息成功:{}", message);
    }

    /**
     * 发送支付通知消息
     */
    public void sendPayNotifyMessage(Long orderId, String payType) {
        String message = String.format("订单[%s]通过%s支付成功,已发送通知", orderId, payType);
        rabbitTemplate.convertAndSend(
                DirectExchangeConfig.DIRECT_EXCHANGE_NAME,
                DirectExchangeConfig.PAY_NOTIFY_ROUTING_KEY,
                message,
                correlationData -> {
                    correlationData.setId(orderId.toString());
                    return correlationData;
                }
        );
        log.info("Direct交换机发送支付通知消息成功:{}", message);
    }
}
5. 消费者(消息接收)
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class DirectConsumer {

    /**
     * 消费订单状态更新消息
     */
    @RabbitListener(queues = DirectExchangeConfig.ORDER_STATUS_QUEUE)
    public void consumeOrderStatusMessage(String message, Channel channel,
                                          @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("接收订单状态消息:{}", message);
            // 模拟业务处理:更新订单状态到数据库
            // orderService.updateStatus(orderId, status);
            channel.basicAck(deliveryTag, false); // 手动ACK确认消费成功
        } catch (Exception e) {
            log.error("消费订单状态消息失败", e);
            // 消费失败,重新入队(最多重试3次,可结合死信队列优化)
            channel.basicNack(deliveryTag, false, true);
        }
    }

    /**
     * 消费支付通知消息
     */
    @RabbitListener(queues = DirectExchangeConfig.PAY_NOTIFY_QUEUE)
    public void consumePayNotifyMessage(String message, Channel channel,
                                        @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("接收支付通知消息:{}", message);
            // 模拟业务处理:发送短信/邮件通知用户
            // notifyService.sendSms(message);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费支付通知消息失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
6. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class DirectTestController {
    private final DirectProducer directProducer;

    @GetMapping("/direct/sendOrderStatus")
    public String sendOrderStatus(@RequestParam Long orderId, @RequestParam String status) {
        directProducer.sendOrderStatusMessage(orderId, status);
        return "订单状态消息发送成功";
    }

    @GetMapping("/direct/sendPayNotify")
    public String sendPayNotify(@RequestParam Long orderId, @RequestParam String payType) {
        directProducer.sendPayNotifyMessage(orderId, payType);
        return "支付通知消息发送成功";
    }
}

2.2 Topic 交换机:模糊路由(多条件匹配)

核心特点
  • 路由规则:支持通配符匹配,路由键和绑定键采用「.」分隔的多级结构(如 order.status.paid);
  • 通配符:* 匹配单个层级,# 匹配零个或多个层级(如绑定键 order.# 可匹配 order.statusorder.status.paid);
  • 本质:多对多的模糊匹配,灵活性最高。
适用场景
  • 多级分类路由:如商品消息按「品类。操作」路由(electronics.addclothes.update);
  • 批量通知:如通知某类用户的所有子系统(user.# 匹配用户相关的所有消息);
  • 日志分级路由:如日志按「系统。级别」路由(order.infopay.error)。
实战源码
1. 交换机与队列配置
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TopicExchangeConfig {
    // 交换机名称
    public static final String TOPIC_EXCHANGE_NAME = "topic_log_exchange";
    // 订单日志队列(匹配order相关所有日志)
    public static final String ORDER_LOG_QUEUE = "order_log_queue";
    // 支付错误日志队列(仅匹配pay的error日志)
    public static final String PAY_ERROR_LOG_QUEUE = "pay_error_log_queue";
    // 绑定键
    public static final String ORDER_LOG_ROUTING_KEY = "order.#"; // 匹配order开头的所有路由键
    public static final String PAY_ERROR_LOG_ROUTING_KEY = "pay.*.error"; // 匹配pay.xxx.error

    // 1. 声明 Topic 交换机
    @Bean
    public TopicExchange topicLogExchange() {
        return ExchangeBuilder.topicExchange(TOPIC_EXCHANGE_NAME)
                .durable(true)
                .autoDelete(false)
                .build();
    }

    // 2. 声明队列
    @Bean
    public Queue orderLogQueue() {
        return QueueBuilder.durable(ORDER_LOG_QUEUE).build();
    }

    @Bean
    public Queue payErrorLogQueue() {
        return QueueBuilder.durable(PAY_ERROR_LOG_QUEUE).build();
    }

    // 3. 绑定
    @Bean
    public Binding orderLogBinding() {
        return BindingBuilder.bind(orderLogQueue()).to(topicLogExchange()).with(ORDER_LOG_ROUTING_KEY);
    }

    @Bean
    public Binding payErrorLogBinding() {
        return BindingBuilder.bind(payErrorLogQueue()).to(topicLogExchange()).with(PAY_ERROR_LOG_ROUTING_KEY);
    }
}
2. 生产者
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class TopicProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送日志消息
     * @param routingKey 路由键(如order.info、pay.system.error)
     * @param logContent 日志内容
     */
    public void sendLog(String routingKey, String logContent) {
        rabbitTemplate.convertAndSend(
                TopicExchangeConfig.TOPIC_EXCHANGE_NAME,
                routingKey,
                logContent,
                correlationData -> {
                    correlationData.setId(System.currentTimeMillis() + "");
                    return correlationData;
                }
        );
        log.info("Topic交换机发送日志消息:routingKey={}, content={}", routingKey, logContent);
    }
}
3. 消费者
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class TopicConsumer {

    /**
     * 消费订单相关所有日志
     */
    @RabbitListener(queues = TopicExchangeConfig.ORDER_LOG_QUEUE)
    public void consumeOrderLog(String message, Channel channel,
                                @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("【订单日志】接收消息:{}", message);
            // 模拟业务:存储订单日志到数据库
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费订单日志失败", e);
            channel.basicNack(deliveryTag, false, false); // 失败不重新入队,后续可结合死信队列
        }
    }

    /**
     * 消费支付错误日志
     */
    @RabbitListener(queues = TopicExchangeConfig.PAY_ERROR_LOG_QUEUE)
    public void consumePayErrorLog(String message, Channel channel,
                                   @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("【支付错误日志】接收消息:{}", message);
            // 模拟业务:发送告警通知运维人员
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费支付错误日志失败", e);
            channel.basicNack(deliveryTag, false, false);
        }
    }
}
4. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class TopicTestController {
    private final TopicProducer topicProducer;

    @GetMapping("/topic/sendLog")
    public String sendLog(@RequestParam String routingKey, @RequestParam String logContent) {
        topicProducer.sendLog(routingKey, logContent);
        return "日志消息发送成功";
    }
}

2.3 Fanout 交换机:广播路由(无差别分发)

核心特点
  • 路由规则:忽略路由键和绑定键,将消息广播到所有与该交换机绑定的队列;
  • 本质:一对多的无差别分发,消息发送效率最高(无需路由匹配)。
适用场景
  • 系统公告:如平台发布全局通知,所有服务都需接收;
  • 数据同步:如用户信息更新后,同步到缓存、搜索、统计等多个子系统;
  • 日志收集:如将应用日志同时发送到日志存储、监控告警、实时分析等队列。
实战源码
1. 交换机与队列配置
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FanoutExchangeConfig {
    // 交换机名称
    public static final String FANOUT_EXCHANGE_NAME = "fanout_notice_exchange";
    // 缓存同步队列
    public static final String CACHE_SYNC_QUEUE = "cache_sync_queue";
    // 搜索同步队列
    public static final String SEARCH_SYNC_QUEUE = "search_sync_queue";
    // 统计同步队列
    public static final String STATISTICS_SYNC_QUEUE = "statistics_sync_queue";

    // 1. 声明 Fanout 交换机
    @Bean
    public FanoutExchange fanoutNoticeExchange() {
        return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE_NAME)
                .durable(true)
                .autoDelete(false)
                .build();
    }

    // 2. 声明队列
    @Bean
    public Queue cacheSyncQueue() {
        return QueueBuilder.durable(CACHE_SYNC_QUEUE).build();
    }

    @Bean
    public Queue searchSyncQueue() {
        return QueueBuilder.durable(SEARCH_SYNC_QUEUE).build();
    }

    @Bean
    public Queue statisticsSyncQueue() {
        return QueueBuilder.durable(STATISTICS_SYNC_QUEUE).build();
    }

    // 3. 绑定(Fanout交换机无需指定绑定键)
    @Bean
    public Binding cacheSyncBinding() {
        return BindingBuilder.bind(cacheSyncQueue()).to(fanoutNoticeExchange());
    }

    @Bean
    public Binding searchSyncBinding() {
        return BindingBuilder.bind(searchSyncQueue()).to(fanoutNoticeExchange());
    }

    @Bean
    public Binding statisticsSyncBinding() {
        return BindingBuilder.bind(statisticsSyncQueue()).to(fanoutNoticeExchange());
    }
}
2. 生产者
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class FanoutProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送用户信息同步通知(广播到所有绑定队列)
     */
    public void sendUserSyncNotice(Long userId, String userName) {
        String message = String.format("用户信息更新:userId=%s, userName=%s", userId, userName);
        // Fanout交换机无需指定路由键(传空字符串即可)
        rabbitTemplate.convertAndSend(
                FanoutExchangeConfig.FANOUT_EXCHANGE_NAME,
                "",
                message,
                correlationData -> {
                    correlationData.setId(userId.toString());
                    return correlationData;
                }
        );
        log.info("Fanout交换机发送用户同步通知:{}", message);
    }
}
3. 消费者
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class FanoutConsumer {

    /**
     * 缓存同步消费
     */
    @RabbitListener(queues = FanoutExchangeConfig.CACHE_SYNC_QUEUE)
    public void consumeCacheSync(String message, Channel channel,
                                 @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("【缓存同步】接收消息:{}", message);
            // 模拟业务:更新Redis缓存中的用户信息
            // redisService.set("user:" + userId, userInfo);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("缓存同步失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }

    /**
     * 搜索同步消费
     */
    @RabbitListener(queues = FanoutExchangeConfig.SEARCH_SYNC_QUEUE)
    public void consumeSearchSync(String message, Channel channel,
                                  @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("【搜索同步】接收消息:{}", message);
            // 模拟业务:更新Elasticsearch中的用户索引
            // esService.updateUserIndex(userId, userInfo);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("搜索同步失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }

    /**
     * 统计同步消费
     */
    @RabbitListener(queues = FanoutExchangeConfig.STATISTICS_SYNC_QUEUE)
    public void consumeStatisticsSync(String message, Channel channel,
                                      @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("【统计同步】接收消息:{}", message);
            // 模拟业务:更新用户统计数据
            // statisticsService.updateUserStat(userId);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("统计同步失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
4. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class FanoutTestController {
    private final FanoutProducer fanoutProducer;

    @GetMapping("/fanout/sendUserSync")
    public String sendUserSync(@RequestParam Long userId, @RequestParam String userName) {
        fanoutProducer.sendUserSyncNotice(userId, userName);
        return "用户同步通知发送成功(所有绑定队列均会接收)";
    }
}

2.4 Headers 交换机:属性路由(非路由键匹配)

核心特点
  • 路由规则:忽略路由键,根据消息的「headers 属性」匹配队列的绑定条件(如 x-match=all 表示所有属性都匹配,x-match=any 表示任意一个属性匹配);
  • 适用场景:消息路由条件不适合用路由键表示的场景(如多维度属性匹配),但实际使用频率低于前三种交换机。
适用场景
  • 多维度过滤:如根据「地区 = 北京」「终端 = APP」「版本 = V2」等多个属性路由消息;
  • 复杂条件路由:路由规则无法通过简单的路由键通配符表达的场景。
实战源码
1. 交换机与队列配置
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class HeadersExchangeConfig {
    // 交换机名称
    public static final String HEADERS_EXCHANGE_NAME = "headers_filter_exchange";
    // APP终端队列(匹配地区=北京且终端=APP)
    public static final String APP_QUEUE = "app_queue";
    // Web终端队列(匹配地区=上海或终端=Web)
    public static final String WEB_QUEUE = "web_queue";

    // 1. 声明 Headers 交换机
    @Bean
    public HeadersExchange headersFilterExchange() {
        return ExchangeBuilder.headersExchange(HEADERS_EXCHANGE_NAME)
                .durable(true)
                .autoDelete(false)
                .build();
    }

    // 2. 声明队列
    @Bean
    public Queue appQueue() {
        return QueueBuilder.durable(APP_QUEUE).build();
    }

    @Bean
    public Queue webQueue() {
        return QueueBuilder.durable(WEB_QUEUE).build();
    }

    // 3. 绑定:APP队列(x-match=all,所有属性都匹配)
    @Bean
    public Binding appBinding() {
        Map<String, Object> headers = new HashMap<>();
        headers.put("region", "Beijing"); // 地区=北京
        headers.put("terminal", "APP"); // 终端=APP
        return BindingBuilder.bind(appQueue())
                .to(headersFilterExchange())
                .whereAll(headers).match(); // 所有属性匹配
    }

    // 4. 绑定:Web队列(x-match=any,任意属性匹配)
    @Bean
    public Binding webBinding() {
        Map<String, Object> headers = new HashMap<>();
        headers.put("region", "Shanghai"); // 地区=上海
        headers.put("terminal", "Web"); // 终端=Web
        return BindingBuilder.bind(webQueue())
                .to(headersFilterExchange())
                .whereAny(headers).match(); // 任意属性匹配
    }
}
2. 生产者
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class HeadersProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送带Headers属性的消息
     */
    public void sendHeadersMessage(String content, String region, String terminal) {
        // 设置消息Headers属性
        MessageProperties properties = new MessageProperties();
        properties.setHeader("region", region);
        properties.setHeader("terminal", terminal);
        Message message = new Message(content.getBytes(), properties);

        // Headers交换机无需指定路由键
        rabbitTemplate.convertAndSend(
                HeadersExchangeConfig.HEADERS_EXCHANGE_NAME,
                "",
                message,
                correlationData -> {
                    correlationData.setId(System.currentTimeMillis() + "");
                    return correlationData;
                }
        );
        log.info("Headers交换机发送消息:content={}, region={}, terminal={}", content, region, terminal);
    }
}
3. 消费者
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class HeadersConsumer {

    /**
     * 消费APP队列消息
     */
    @RabbitListener(queues = HeadersExchangeConfig.APP_QUEUE)
    public void consumeAppQueue(Message message, Channel channel,
                                @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            String content = new String(message.getBody());
            String region = (String) message.getMessageProperties().getHeader("region");
            String terminal = (String) message.getMessageProperties().getHeader("terminal");
            log.info("【APP队列】接收消息:content={}, region={}, terminal={}", content, region, terminal);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费APP队列消息失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }

    /**
     * 消费Web队列消息
     */
    @RabbitListener(queues = HeadersExchangeConfig.WEB_QUEUE)
    public void consumeWebQueue(Message message, Channel channel,
                                @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            String content = new String(message.getBody());
            String region = (String) message.getMessageProperties().getHeader("region");
            String terminal = (String) message.getMessageProperties().getHeader("terminal");
            log.info("【Web队列】接收消息:content={}, region={}, terminal={}", content, region, terminal);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费Web队列消息失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
4. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class HeadersTestController {
    private final HeadersProducer headersProducer;

    @GetMapping("/headers/sendMessage")
    public String sendMessage(@RequestParam String content,
                              @RequestParam String region,
                              @RequestParam String terminal) {
        headersProducer.sendHeadersMessage(content, region, terminal);
        return "Headers消息发送成功";
    }
}

三、RabbitMQ 常用队列类型:场景与实现

除了基础队列,RabbitMQ 提供了多种特殊队列,满足复杂业务需求。以下介绍四种核心队列的使用场景与源码实现。

3.1 死信队列(DLX):失败消息处理

核心概念
  • 死信:无法被消费的消息(如消费重试次数耗尽、消息过期、队列达到最大长度);
  • 死信队列:存储死信的队列,需通过「死信交换机(DLX)」路由死信;
  • 核心价值:避免失败消息丢失,便于后续排查问题或人工重试。
适用场景
  • 订单支付超时未支付(消息过期后进入死信队列,触发订单关闭);
  • 消费失败的消息(重试 3 次后进入死信队列,避免重复重试占用资源);
  • 队列满了无法接收新消息(超出长度的消息进入死信队列,避免消息丢失)。
实战源码(基于 Direct 交换机实现)
1. 配置类
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DeadLetterQueueConfig {
    // 普通交换机
    public static final String NORMAL_EXCHANGE_NAME = "normal_order_exchange";
    // 死信交换机
    public static final String DLX_EXCHANGE_NAME = "dlx_order_exchange";
    // 普通队列(订单支付队列)
    public static final String NORMAL_QUEUE_NAME = "normal_order_queue";
    // 死信队列(订单关闭队列)
    public static final String DLX_QUEUE_NAME = "dlx_order_queue";
    // 绑定键
    public static final String NORMAL_ROUTING_KEY = "order.pay";
    public static final String DLX_ROUTING_KEY = "order.dlx";

    // 1. 声明死信交换机(Direct类型)
    @Bean
    public DirectExchange dlxOrderExchange() {
        return ExchangeBuilder.directExchange(DLX_EXCHANGE_NAME).durable(true).build();
    }

    // 2. 声明死信队列
    @Bean
    public Queue dlxOrderQueue() {
        return QueueBuilder.durable(DLX_QUEUE_NAME).build();
    }

    // 3. 绑定死信交换机与死信队列
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(dlxOrderQueue()).to(dlxOrderExchange()).with(DLX_ROUTING_KEY);
    }

    // 4. 声明普通队列(配置死信相关参数)
    @Bean
    public Queue normalOrderQueue() {
        return QueueBuilder.durable(NORMAL_QUEUE_NAME)
                .withArgument("x-dead-letter-exchange", DLX_EXCHANGE_NAME) // 绑定死信交换机
                .withArgument("x-dead-letter-routing-key", DLX_ROUTING_KEY) // 死信路由键
                .withArgument("x-message-ttl", 60000) // 消息过期时间(1分钟)
                .withArgument("x-max-length", 1000) // 队列最大长度(1000条)
                .build();
    }

    // 5. 声明普通交换机
    @Bean
    public DirectExchange normalOrderExchange() {
        return ExchangeBuilder.directExchange(NORMAL_EXCHANGE_NAME).durable(true).build();
    }

    // 6. 绑定普通交换机与普通队列
    @Bean
    public Binding normalBinding() {
        return BindingBuilder.bind(normalOrderQueue()).to(normalOrderExchange()).with(NORMAL_ROUTING_KEY);
    }
}
2. 生产者(发送订单支付消息)
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class DlxProducer {
    private final RabbitTemplate rabbitTemplate;

    public void sendOrderPayMessage(Long orderId) {
        String message = String.format("订单[%s]待支付,1分钟内未支付将自动关闭", orderId);
        rabbitTemplate.convertAndSend(
                DeadLetterQueueConfig.NORMAL_EXCHANGE_NAME,
                DeadLetterQueueConfig.NORMAL_ROUTING_KEY,
                message,
                correlationData -> {
                    correlationData.setId(orderId.toString());
                    return correlationData;
                }
        );
        log.info("发送订单支付消息:{}", message);
    }
}
3. 消费者(普通队列消费 + 死信队列消费)
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class DlxConsumer {
    // 重试次数上限
    private static final int MAX_RETRY_COUNT = 3;

    /**
     * 消费普通队列(订单支付消息)
     */
    @RabbitListener(queues = DeadLetterQueueConfig.NORMAL_QUEUE_NAME)
    public void consumeNormalQueue(String message, Channel channel,
                                   @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
                                   @Header(AmqpHeaders.REDELIVERED) boolean redelivered) throws Exception {
        // 解析订单ID(实际业务中可通过消息体传递)
        String orderId = message.split("\[")[1].split("\]")[0];
        int retryCount = redelivered ? 1 : 0; // 重试次数(redelivered为true表示重新入队)

        try {
            log.info("接收订单支付消息:{},重试次数:{}", message, retryCount);
            // 模拟业务:查询订单支付状态
            // boolean isPaid = orderService.checkPayStatus(Long.parseLong(orderId));
            // if (isPaid) {
            //     channel.basicAck(deliveryTag, false); // 已支付,确认消费
            // } else {
            //     throw new RuntimeException("订单未支付,需要重试");
            // }

            // 模拟消费失败(实际业务中根据支付状态判断)
            throw new RuntimeException("订单未支付");
        } catch (Exception e) {
            log.error("消费订单支付消息失败", e);
            if (retryCount >= MAX_RETRY_COUNT) {
                // 重试次数耗尽,拒绝消息,进入死信队列
                log.info("重试次数耗尽,消息进入死信队列:{}", message);
                channel.basicReject(deliveryTag, false);
            } else {
                // 重新入队,重试
                channel.basicNack(deliveryTag, false, true);
            }
        }
    }

    /**
     * 消费死信队列(订单关闭消息)
     */
    @RabbitListener(queues = DeadLetterQueueConfig.DLX_QUEUE_NAME)
    public void consumeDlxQueue(String message, Channel channel,
                                @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("接收死信消息,执行订单关闭:{}", message);
            // 模拟业务:关闭订单、释放库存
            // orderService.closeOrder(Long.parseLong(orderId));
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("处理死信消息失败", e);
            channel.basicAck(deliveryTag, false); // 死信队列消息不再重试,避免死循环
        }
    }
}

3.2 延迟队列:定时任务实现

核心概念
  • 延迟队列:消息发送后,延迟指定时间才被消费的队列;

  • 实现方式:

    1. TTL + 死信队列(基于 RabbitMQ 原生功能,适用于延迟时间固定的场景);
    2. 延迟交换机插件(rabbitmq-delayed-message-exchange,支持动态延迟时间,推荐生产使用)。
适用场景
  • 订单超时关闭(延迟 30 分钟);
  • 定时通知(如会议开始前 10 分钟提醒);
  • 任务重试(延迟 5 分钟后重试失败任务)。
实战源码(基于延迟插件实现)
1. 安装延迟插件
  • 下载插件:从 RabbitMQ 官方插件库 下载 rabbitmq_delayed_message_exchange 插件;
  • 安装插件:将插件复制到 RabbitMQ 插件目录(如 /usr/lib/rabbitmq/lib/rabbitmq_server-3.12.0/plugins),执行命令 rabbitmq-plugins enable rabbitmq_delayed_message_exchange
  • 重启 RabbitMQ:systemctl restart rabbitmq-server
2. 配置类
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DelayedQueueConfig {
    // 延迟交换机名称
    public static final String DELAYED_EXCHANGE_NAME = "delayed_notice_exchange";
    // 延迟队列名称
    public static final String DELAYED_QUEUE_NAME = "delayed_notice_queue";
    // 绑定键
    public static final String DELAYED_ROUTING_KEY = "notice.delayed";

    // 1. 声明延迟交换机(类型为 x-delayed-message)
    @Bean
    public CustomExchange delayedNoticeExchange() {
        // 配置交换机类型和延迟参数
        return new CustomExchange(
                DELAYED_EXCHANGE_NAME,
                "x-delayed-message", // 延迟交换机类型
                true, // 持久化
                false, // 自动删除
                Map.of("x-delayed-type", "direct") // 延迟交换机的路由类型(Direct/Topic等)
        );
    }

    // 2. 声明延迟队列
    @Bean
    public Queue delayedNoticeQueue() {
        return QueueBuilder.durable(DELAYED_QUEUE_NAME).build();
    }

    // 3. 绑定延迟交换机与队列
    @Bean
    public Binding delayedBinding() {
        return BindingBuilder.bind(delayedNoticeQueue())
                .to(delayedNoticeExchange())
                .with(DELAYED_ROUTING_KEY)
                .noargs();
    }
}
3. 生产者
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class DelayedProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送延迟消息
     * @param content 消息内容
     * @param delayTime 延迟时间(毫秒)
     */
    public void sendDelayedMessage(String content, long delayTime) {
        rabbitTemplate.convertAndSend(
                DelayedQueueConfig.DELAYED_EXCHANGE_NAME,
                DelayedQueueConfig.DELAYED_ROUTING_KEY,
                content,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // 设置延迟时间(核心)
                        message.getMessageProperties().setHeader("x-delay", delayTime);
                        return message;
                    }
                },
                correlationData -> {
                    correlationData.setId(System.currentTimeMillis() + "");
                    return correlationData;
                }
        );
        log.info("发送延迟消息:content={}, 延迟时间={}ms", content, delayTime);
    }
}
4. 消费者
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class DelayedConsumer {

    @RabbitListener(queues = DelayedQueueConfig.DELAYED_QUEUE_NAME)
    public void consumeDelayedQueue(String message, Channel channel,
                                    @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws Exception {
        try {
            log.info("延迟消息到期,接收消息:{}", message);
            // 模拟业务:发送定时通知
            // notifyService.sendReminder(message);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费延迟消息失败", e);
            channel.basicNack(deliveryTag, false, false);
        }
    }
}
5. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class DelayedTestController {
    private final DelayedProducer delayedProducer;

    @GetMapping("/delayed/sendMessage")
    public String sendMessage(@RequestParam String content, @RequestParam long delayTime) {
        delayedProducer.sendDelayedMessage(content, delayTime);
        return "延迟消息发送成功";
    }
}

3.3 优先级队列:消息优先级排序

核心概念
  • 优先级队列:支持为消息设置优先级(0-255,数值越大优先级越高),消费者优先消费高优先级消息;
  • 核心价值:确保重要消息被优先处理(如 VIP 用户的订单、紧急告警)。
适用场景
  • 订单优先级:VIP 用户的订单优先处理;
  • 告警优先级:严重告警(P0 级)优先于普通告警(P3 级);
  • 任务优先级:紧急任务(如数据修复)优先于普通任务(如数据统计)。
实战源码
1. 配置类
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PriorityQueueConfig {
    // 交换机名称
    public static final String PRIORITY_EXCHANGE_NAME = "priority_order_exchange";
    // 优先级队列名称
    public static final String PRIORITY_QUEUE_NAME = "priority_order_queue";
    // 绑定键
    public static final String PRIORITY_ROUTING_KEY = "order.priority";

    // 1. 声明交换机(Direct类型)
    @Bean
    public DirectExchange priorityOrderExchange() {
        return ExchangeBuilder.directExchange(PRIORITY_EXCHANGE_NAME).durable(true).build();
    }

    // 2. 声明优先级队列(设置最大优先级)
    @Bean
    public Queue priorityOrderQueue() {
        return QueueBuilder.durable(PRIORITY_QUEUE_NAME)
                .withArgument("x-max-priority", 10) // 最大优先级(0-10,数值越大优先级越高)
                .build();
    }

    // 3. 绑定
    @Bean
    public Binding priorityBinding() {
        return BindingBuilder.bind(priorityOrderQueue()).to(priorityOrderExchange()).with(PRIORITY_ROUTING_KEY);
    }
}
2. 生产者
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class PriorityProducer {
    private final RabbitTemplate rabbitTemplate;

    /**
     * 发送带优先级的订单消息
     * @param orderId 订单ID
     * @param userType 用户类型(VIP/NORMAL)
     * @param priority 优先级(1-10)
     */
    public void sendPriorityOrderMessage(Long orderId, String userType, int priority) {
        String message = String.format("用户类型:%s,订单[%s]待处理", userType, orderId);
        rabbitTemplate.convertAndSend(
                PriorityQueueConfig.PRIORITY_EXCHANGE_NAME,
                PriorityQueueConfig.PRIORITY_ROUTING_KEY,
                message,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // 设置消息优先级
                        message.getMessageProperties().setPriority(priority);
                        return message;
                    }
                },
                correlationData -> {
                    correlationData.setId(orderId.toString());
                    return correlationData;
                }
        );
        log.info("发送优先级订单消息:content={}, 优先级={}", message, priority);
    }
}
3. 消费者
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import com.rabbitmq.client.Channel;

@Component
@Slf4j
public class PriorityConsumer {

    @RabbitListener(queues = PriorityQueueConfig.PRIORITY_QUEUE_NAME)
    public void consumePriorityQueue(String message, Channel channel,
                                     @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
                                     @Header("priority") Integer priority) throws Exception {
        try {
            log.info("接收优先级订单消息:content={}, 优先级={}", message, priority);
            // 模拟业务:处理订单(高优先级订单优先执行)
            // orderService.processOrder(orderId);
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            log.error("消费优先级订单消息失败", e);
            channel.basicNack(deliveryTag, false, true);
        }
    }
}
4. 测试接口
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class PriorityTestController {
    private final PriorityProducer priorityProducer;

    @GetMapping("/priority/sendOrder")
    public String sendOrder(@RequestParam Long orderId,
                            @RequestParam String userType,
                            @RequestParam int priority) {
        priorityProducer.sendPriorityOrderMessage(orderId, userType, priority);
        return "优先级订单消息发送成功";
    }
}

3.4 镜像队列:高可用部署

核心概念
  • 镜像队列:将队列复制到 RabbitMQ 集群的多个节点,当主节点宕机时,从节点自动接管队列,确保服务不中断;
  • 核心价值:避免队列单点故障,提升 RabbitMQ 集群的高可用性。
适用场景
  • 生产环境部署:所有核心业务队列(如订单、支付)都需配置镜像队列;
  • 高可用要求高的场景:不允许因节点宕机导致队列不可用。
实现方式(集群配置)
  1. 搭建 RabbitMQ 集群(略,参考 RabbitMQ 官方文档);

  2. 配置镜像队列策略

    • 命令行方式:

      # 为所有队列配置镜像策略(复制到所有节点)
      rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
      # 为特定队列配置镜像策略(复制到2个节点)
      rabbitmqctl set_policy ha-two "^ha." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
      
    • 管理界面方式:进入 RabbitMQ Management → Admin → Policies → Add /update a policy,配置策略参数:

      • Name:策略名称(如 ha-all);
      • Pattern:队列名称匹配规则(如 ^ 匹配所有队列);
      • Apply to:Queues;
      • Definition:ha-mode=all(复制到所有节点)、ha-sync-mode=automatic(自动同步队列数据)。

四、RabbitMQ 典型使用场景与解决方案

4.1 场景 1:异步通信(服务解耦)

业务痛点
  • 订单系统创建订单后,需调用支付、库存、日志、通知等多个服务,同步调用导致响应慢、耦合度高;
  • 某一个服务故障(如通知服务宕机),会导致整个订单流程失败。
解决方案
  • 采用 RabbitMQ 异步通信,订单系统发送消息后直接返回,其他服务异步消费;
  • 交换机选型:Direct 交换机(精准路由到不同服务队列)。
架构设计
订单系统(生产者)→ Direct交换机 → 库存队列(库存服务)
                                  → 支付队列(支付服务)
                                  → 通知队列(通知服务)
                                  → 日志队列(日志服务)
核心源码

参考 2.1 节 Direct 交换机的实战源码,只需扩展队列和消费者即可。

4.2 场景 2:削峰填谷(秒杀高并发)

业务痛点
  • 秒杀活动峰值 QPS 达 10 万 +,直接调用数据库会导致数据库宕机;
  • 大量请求同时到达,服务线程池耗尽,导致服务不可用。
解决方案
  • 采用 RabbitMQ 作为缓冲,秒杀请求先写入队列,消费者按数据库处理能力平滑消费;
  • 交换机选型:Direct 交换机;
  • 队列配置:普通队列 + 限流(消费者 prefetch 配置为 10,控制每秒消费速度)。
架构设计
用户秒杀请求 → API网关 → 秒杀服务(生产者)→ Direct交换机 → 秒杀队列 → 秒杀消费者(按能力消费)→ 数据库
核心优化
  • 生产者限流:API 网关层限制每秒请求数,避免队列堆积过多;
  • 消费者限流:spring.rabbitmq.listener.simple.prefetch=10(每个消费者每次拉取 10 条消息);
  • 队列持久化:避免服务宕机导致消息丢失。

4.3 场景 3:广播通知(系统公告)

业务痛点
  • 系统发布全局公告,需通知所有在线服务(如缓存服务、搜索服务、统计服务);
  • 新增服务后,需修改发布公告的代码,耦合度高。
解决方案
  • 采用 Fanout 交换机,发布公告时广播到所有绑定队列,新增服务只需绑定队列即可;
  • 交换机选型:Fanout 交换机。
架构设计
公告服务(生产者)→ Fanout交换机 → 缓存服务队列
                                  → 搜索服务队列
                                  → 统计服务队列
                                  → 其他服务队列
核心源码

参考 2.3 节 Fanout 交换机的实战源码。

4.4 场景 4:定时任务(订单超时关闭)

业务痛点
  • 订单创建后 30 分钟未支付需自动关闭,传统定时任务(如 Quartz)存在精度低、资源占用高的问题;
  • 大量订单同时超时,导致数据库压力突增。
解决方案
  • 采用 RabbitMQ 延迟队列,订单创建时发送延迟 30 分钟的消息,到期后消费消息关闭订单;
  • 实现方式:延迟交换机插件(支持动态延迟时间)。
架构设计
订单系统(生产者)→ 延迟交换机 → 延迟队列 → 消费者(到期关闭订单)→ 数据库
核心源码

参考 3.2 节延迟队列的实战源码。

五、RabbitMQ 可靠性与性能优化

5.1 可靠性保障

  1. 消息持久化:交换机、队列、消息均设置为持久化(durable=true),避免 RabbitMQ 重启后消息丢失;
  2. 生产者确认:开启 publisher-confirm-type: correlated,确保消息成功发送到交换机;
  3. 消费者 ACK:开启手动 ACK(acknowledge-mode: manual),避免消费失败导致消息丢失;
  4. 死信队列:处理无法消费的消息,便于排查问题;
  5. 幂等性处理:通过消息 ID 去重(如数据库唯一键、Redis 缓存),避免重复消费。

5.2 性能优化

  1. 连接池优化:使用 Spring AMQP 自带的连接池,配置合理的最大连接数和通道数;
  2. 消费者限流:通过 prefetch 参数控制消费者每次拉取的消息数,避免消费者过载;
  3. 序列化优化:使用 Protobuf 替代 JSON,减少消息体积,提升序列化效率;
  4. 队列拆分:将不同业务的队列拆分到不同交换机,避免队列堆积影响其他业务;
  5. 集群部署:采用 RabbitMQ 集群 + 镜像队列,提升并发处理能力和高可用性。

六、总结与最佳实践

RabbitMQ 的核心优势在于灵活的路由机制(四种交换机)和丰富的队列类型(死信、延迟、优先级队列),能够适配多种分布式场景。使用时需遵循以下最佳实践:

  1. 交换机选型:精准路由用 Direct,模糊路由用 Topic,广播用 Fanout,多属性匹配用 Headers;
  2. 队列选型:失败消息用死信队列,定时任务用延迟队列,重要消息用优先级队列;
  3. 可靠性优先:生产环境必须开启持久化、生产者确认、消费者 ACK、死信队列;
  4. 性能优化:合理配置连接池、prefetch 参数,避免队列堆积,采用集群部署提升并发;
  5. 监控运维:使用 RabbitMQ Management 监控队列堆积、消息吞吐量,结合 Prometheus + Grafana 实现告警。

本文提供的源码可直接复用,只需根据实际业务调整交换机、队列名称和业务逻辑即可快速落地。RabbitMQ 作为成熟的消息中间件,在分布式系统中应用广泛,掌握其核心特性和实战技巧,能有效解决异步通信、解耦、削峰填谷等核心问题。