首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深入浅出 RabbitMQ - 主题模式(Topic)

深入浅出 RabbitMQ - 主题模式(Topic)

作者头像
工藤学编程
发布2025-12-22 09:35:32
发布2025-12-22 09:35:32
1590
举报

大家好,我是工藤学编程 🦉

一个正在努力学习的小博主,期待你的关注

实战代码系列最新文章😉

C++实现图书管理系统(Qt C++ GUI界面版)

SpringBoot实战系列🐷

【SpringBoot实战系列】SpringBoot3.X 整合 MinIO 存储原生方案

分库分表

分库分表之实战-sharding-JDBC分库分表执行流程原理剖析

消息队列

深入浅出 RabbitMQ-路由模式详解


前情摘要:

1、深入浅出 RabbitMQ-核心概念介绍与容器化部署 2、深入浅出 RabbitMQ-简单队列实战 3、深入浅出 RabbitMQ-工作队列实战(轮训策略VS公平策略) 4、深入浅出 RabbitMQ-交换机详解与发布订阅模型实战 4、深入浅出 RabbitMQ-路由模式详解


【亲测宝藏】发现一个让 AI 学习秒变轻松的神站!不用啃高数、不用怕编程,高中生都能看懂的人工智能教程来啦!

👉点击跳转,和 thousands of 小伙伴一起用快乐学习法征服 AI,说不定下一个开发出爆款 AI 程序的就是你!


深入浅出 RabbitMQ-主题模式(Topic):通配符带来的灵活消息分发

在 RabbitMQ 的消息分发机制中,如果你觉得路由模式(Direct)的精确匹配不够灵活,又嫌发布订阅模式(Fanout)的广播太过“粗放”,那么主题模式(Topic) 一定是你的理想选择。它像一把“瑞士军刀”,通过通配符匹配实现了更精细、更灵活的消息路由,几乎能满足大多数复杂业务场景的需求。

一、为什么需要主题模式?

先来看一个实际问题:如果你的系统中有上百种消息类型,每种类型对应一个路由键(Routing Key),用路由模式(Direct)需要为每个路由键手动绑定队列,维护成本会非常高。比如日志系统中,可能有 order.log.erroruser.log.infoproduct.log.debug 等成百上千种路由键,总不能逐个绑定吧?

主题模式应运而生:它通过「通配符匹配」替代精确匹配,让队列只需绑定一个或几个带通配符的路由键,就能匹配一类消息,极大减少了绑定工作量,同时保留了消息定向分发的灵活性。

二、什么是主题模式?

主题模式是 RabbitMQ 中最灵活的消息分发模式,核心是基于通配符的路由键(Routing Key)匹配。其关键特性如下:

  • 交换机类型:必须使用 Topic 类型(主题交换机)。
  • 路由键格式:路由键由多个“词”组成,词之间用 . 分隔(如 order.log.error,其中 orderlogerror 是三个词)。
  • 通配符规则:队列与交换机绑定时,Binding Key 可使用通配符:
    • *:匹配恰好1个词(如 *.log.error 可匹配 order.log.error,但不能匹配 user.system.log.error)。
    • #:匹配1个或多个词(如 order.log.# 可匹配 order.log.errororder.log.info.debug 等)。
  • 转发逻辑:Topic 交换机会将消息的 Routing Key 与队列的 Binding Key 进行通配符匹配,匹配成功则转发消息。
三、通配符匹配规则实战解析

光说规则太抽象,我们用几个例子直观感受下(假设消息的 Routing Key 如下,队列的 Binding Key 为 Q1: *.log.*Q2: order.log.#):

消息 Routing Key

是否匹配 Q1(.log.)

是否匹配 Q2(order.log.#)

结果总结

order.log.error

是(order 匹配 *)

是(# 匹配 error)

同时进入 Q1 和 Q2

user.log.info

是(user 匹配 *)

否(前缀不是 order)

仅进入 Q1

order.log.debug.warn

否(超过3个词)

是(# 匹配 debug.warn)

仅进入 Q2

product.error

否(没有 log 词)

否(前缀不是 order)

不匹配任何队列,默认被丢弃

注意:如果消息的 Routing Key 没有匹配到任何队列,Topic 交换机会默认丢弃消息(可通过设置「返回回调」监听未被路由的消息,进行二次处理)。

四、经典应用场景:精细化日志系统

主题模式最典型的场景是日志分级分类处理,比如:

  • 需求1:收集所有系统的 error 级别日志(用于告警),Binding Key 可设为 *.log.error
  • 需求2:收集订单系统的所有级别日志(用于订单问题排查),Binding Key 可设为 order.log.#
  • 需求3:收集所有系统的所有日志(用于全局存储),Binding Key 可设为 #.log.#

只需一个 Topic 交换机,就能通过不同通配符绑定,实现多维度的日志分发,远比路由模式更简洁。

五、代码实战:实现多维度日志分发

下面用 Java 代码演示主题模式的完整流程(基于 RabbitMQ Java Client 5.x)。

1. 生产者:发送带结构化路由键的日志
代码语言:javascript
复制
public class LogProducer {
    private static final String EXCHANGE_NAME = "log_topic_exchange";

    public static void main(String[] args) throws Exception {
        // 1. 配置连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.229.128");
        factory.setUsername("admin");
        factory.setPassword("password");
        factory.setVirtualHost("/dev");
        factory.setPort(5672);

        // 2. 建立连接和信道
        try (Connection connection = factory.newConnection();
             Channel channel = connection.createChannel()) {

            // 3. 声明 Topic 交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

            // 4. 发送不同类型的日志(路由键格式:[系统名].[日志类型].[级别])
            String orderErrorLog = "订单系统:支付超时";
            String orderInfoLog = "订单系统:新订单创建";
            String productDebugLog = "商品系统:库存查询SQL执行";

            // 5. 指定具体的 Routing Key(无通配符)
            channel.basicPublish(EXCHANGE_NAME, "order.log.error", null, orderErrorLog.getBytes());
            channel.basicPublish(EXCHANGE_NAME, "order.log.info", null, orderInfoLog.getBytes());
            channel.basicPublish(EXCHANGE_NAME, "product.log.debug", null, productDebugLog.getBytes());

            System.out.println("日志消息发送完成");
        }
    }
}
2. 消费者1:接收订单系统所有日志(order.log.#)
代码语言:javascript
复制
public class OrderLogConsumer {
    private static final String EXCHANGE_NAME = "log_topic_exchange";

    public static void main(String[] args) throws Exception {
        // 连接配置同生产者
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.229.128");
        // ...省略其他配置

        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        // 声明交换机(与生产者一致)
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        // 创建临时队列,绑定通配符 Binding Key:order.log.#
        String queueName = channel.queueDeclare().getQueue();
        channel.queueBind(queueName, EXCHANGE_NAME, "order.log.#");

        // 消费消息(订单系统日志处理逻辑)
        DeliverCallback callback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("[订单日志服务] 收到:" + message + "(路由键:" + delivery.getEnvelope().getRoutingKey() + ")");
        };
        channel.basicConsume(queueName, true, callback, consumerTag -> {});
    }
}
3. 消费者2:接收所有系统的 error 日志(*.log.error)
代码语言:javascript
复制
public class ErrorAlertConsumer {
    private static final String EXCHANGE_NAME = "log_topic_exchange";

    public static void main(String[] args) throws Exception {
        // 连接配置同生产者
        ConnectionFactory factory = new ConnectionFactory();
        // ...省略其他配置

        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        String queueName = channel.queueDeclare().getQueue();

        // 绑定通配符 Binding Key:*.log.error
        channel.queueBind(queueName, EXCHANGE_NAME, "*.log.error");

        // 消费消息(错误告警逻辑)
        DeliverCallback callback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("[告警服务] 紧急!" + message + "(路由键:" + delivery.getEnvelope().getRoutingKey() + ")");
        };
        channel.basicConsume(queueName, true, callback, consumerTag -> {});
    }
}
六、运行效果与核心优势
  • 生产者发送3条消息后:
    • 「订单日志服务」会收到 order.log.errororder.log.info(匹配 order.log.#)。
    • 「告警服务」只会收到 order.log.error(匹配 *.log.error)。
    • product.log.debug 因无匹配队列,默认被丢弃(可通过回调处理)。
  • 核心优势:
    1. 灵活性:通配符匹配覆盖多种场景,减少绑定工作量。
    2. 兼容性:可模拟 Fanout(用 # 匹配所有)和 Direct(用具体路由键)的功能。
    3. 可扩展性:新增消息类型时,无需修改现有绑定,只需按规则定义路由键即可。
七、RabbitMQ 五种工作模式总结

学到这里,我们已经掌握了 RabbitMQ 的核心工作模式,这里用一张表总结它们的区别:

模式

交换机类型

路由键(Routing Key)使用方式

核心场景

特点总结

简单模式

默认交换机

无需指定(队列名即路由键)

一对一消息传递

最简单,无交换机配置

工作队列

默认交换机

无需指定(队列名即路由键)

多消费者分摊任务

支持轮训/公平分发策略

发布订阅

Fanout

无需指定(忽略路由键,广播所有队列)

消息通知、日志广播

所有绑定队列都能收到消息

路由模式

Direct

精确匹配(消息路由键 = 队列绑定键)

按类型分发(如 error/info)

精准匹配,适合少量固定类型

主题模式

Topic

通配符匹配(* 匹配1个词,# 匹配多个词)

复杂分类场景(如多系统日志)

最灵活,支持规则化匹配

八、总结

主题模式(Topic)凭借通配符匹配的特性,成为 RabbitMQ 中应用最广泛的模式之一。它既解决了路由模式(Direct)绑定繁琐的问题,又弥补了发布订阅模式(Fanout)无法精确筛选的缺陷,几乎能应对所有需要“规则化消息分发”的场景。

掌握主题模式的关键,在于理解 *# 的区别,以及路由键的结构化设计(建议按业务维度用 . 分隔,如 系统.模块.操作)。下次开发时,不妨优先考虑主题模式——它可能就是你寻找的“万能方案”。

觉得有用请点赞收藏! 如果有相关问题,欢迎评论区留言讨论~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 深入浅出 RabbitMQ-主题模式(Topic):通配符带来的灵活消息分发
    • 一、为什么需要主题模式?
    • 二、什么是主题模式?
    • 三、通配符匹配规则实战解析
    • 四、经典应用场景:精细化日志系统
    • 五、代码实战:实现多维度日志分发
      • 1. 生产者:发送带结构化路由键的日志
      • 2. 消费者1:接收订单系统所有日志(order.log.#)
      • 3. 消费者2:接收所有系统的 error 日志(*.log.error)
    • 六、运行效果与核心优势
    • 七、RabbitMQ 五种工作模式总结
    • 八、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档