基于AMQP协议模拟MQTT的发布 / 订阅主题功能

1. MQTT 的主题模式

MQTT 的核心就是 发布 / 订阅主题

  • 客户端直接 PUBLISH 到一个主题(例如 sensor/temp)。
  • 其他客户端 SUBSCRIBE 同一个主题或通配符主题(例如 sensor/#)。
  • Broker 负责将消息推送给所有订阅了匹配主题的客户端。
  • 主题是轻量级的字符串,支持层级(/ 分隔)和通配符(+ 单层,# 多层)。
  • 不需要提前声明主题,客户端可以随时发布或订阅。

2. AMQP 的类似机制

AMQP 没有 "主题" 这个概念,但可以通过 交换机(Exchange)+ 队列(Queue)+ 绑定(Binding) 来模拟 MQTT 的主题效果:

1.使用 topic 类型交换机

  • 交换机类型topic

  • 路由键(Routing Key) :用点 . 分隔,类似 MQTT 的主题层级,例如:

    plaintext

    复制代码
    sensor.temp
    sensor.humidity
    device.control
  • 绑定键(Binding Key) :支持通配符:

    • * 匹配一个单词(层级)
    • # 匹配零个或多个单词(层级)

例子

  • 生产者发送消息到交换机 my_topic_exchange,路由键 sensor.temp
  • 队列 topic_queue 绑定到交换机,绑定键 sensor.# → 会收到所有 sensor.xxx 的消息。

3.交换机、队列和绑定关系的示意图

示意图说明

  • 交换机my_topic_exchange(类型 topic
  • 队列topic_queue
  • 绑定键sensor.#(只匹配以 sensor 开头的主题)
  • 生产者 发布不同主题的消息:
    • sensor.temp → 消费者收到
    • sensor.humidity → 消费者收到
    • device.control → 消费者不会收到 (因为绑定键只匹配 sensor 开头的主题)

4.Python程序

  1. setup_topic.py:负责初始化 RabbitMQ 的交换机、队列和绑定规则,是整个消息系统的基础。
  2. producer_topic.py:负责发送消息到交换机,每条消息可以指定不同的主题。
  3. consumer_topic.py :负责从队列中接收并处理消息,但只订阅特定主题(sensor 开头)。

1. setup_topic.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
setup_topic.py - 一次性创建 RabbitMQ Topic 交换机、队列和绑定关系

功能说明:
1. 连接到 RabbitMQ Broker
2. 创建 topic 类型交换机(持久化)
3. 创建队列(持久化)
4. 将队列绑定到交换机,只接收 sensor 开头的主题
"""

import pika

# ======================== 配置参数 ========================
HOST = 'localhost'              # RabbitMQ 服务器的 IP 地址
PORT = 5672                     # RabbitMQ 默认 AMQP 端口
USER = 'mi_user'                # 用户名
PASSWORD = 'YourPassword123'    # 密码
EXCHANGE_NAME = 'my_topic_exchange'  # 交换机名称
QUEUE_NAME = 'topic_queue'           # 队列名称
BINDING_KEY = 'sensor.#'              # 绑定键(只匹配 sensor 开头的主题)

try:
    # 1. 创建连接(BlockingConnection 表示同步阻塞模式)
    conn = pika.BlockingConnection(
        pika.ConnectionParameters(
            host=HOST,
            port=PORT,
            credentials=pika.PlainCredentials(USER, PASSWORD)  # 用户名密码认证
        )
    )

    # 2. 创建一个信道(Channel),所有操作都在信道中进行
    ch = conn.channel()
    print(f"✅ 连接成功 {HOST}:{PORT}")

    # 3. 创建 topic 交换机
    #    exchange_type='topic':支持通配符的主题交换机
    #    durable=True:交换机持久化,RabbitMQ 重启后仍然存在
    ch.exchange_declare(
        exchange=EXCHANGE_NAME,
        exchange_type='topic',
        durable=True
    )
    print(f"✅ 交换机 '{EXCHANGE_NAME}' 已创建")

    # 4. 创建队列
    #    durable=True:队列持久化,RabbitMQ 重启后仍然存在
    ch.queue_declare(queue=QUEUE_NAME, durable=True)
    print(f"✅ 队列 '{QUEUE_NAME}' 已创建")

    # 5. 绑定队列到交换机
    #    routing_key=BINDING_KEY:绑定键(交换机根据绑定键过滤消息)
    #    'sensor.#':匹配所有以 sensor 开头的主题(包括多层级)
    ch.queue_bind(
        exchange=EXCHANGE_NAME,
        queue=QUEUE_NAME,
        routing_key=BINDING_KEY
    )
    print(f"✅ 已绑定队列到交换机,绑定键: {BINDING_KEY}")

    # 6. 关闭连接
    conn.close()
    print("\n✅ 基础设施配置完成!")

except Exception as e:
    # 捕获并处理异常
    print(f"❌ 配置失败: {e}")

2. producer_topic.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
producer_topic.py - 发布消息到不同的主题

功能说明:
1. 连接到 RabbitMQ Broker
2. 使用 topic 交换机发布消息
3. 支持不同主题(路由键)
"""

import pika
import json

# ======================== 配置参数 ========================
HOST = 'localhost'              # RabbitMQ 服务器的 IP 地址
PORT = 5672                     # RabbitMQ 默认 AMQP 端口
USER = 'mi_user'                # 用户名
PASSWORD = 'YourPassword123'    # 密码
EXCHANGE_NAME = 'my_topic_exchange'  # 交换机名称

def send_to_topic(topic, message):
    """
    发送消息到指定主题(路由键)
    :param topic: 主题字符串,例如 "sensor.temp"
    :param message: 消息内容(Python 字典)
    """
    # 1. 创建连接
    conn = pika.BlockingConnection(
        pika.ConnectionParameters(
            host=HOST,
            port=PORT,
            credentials=pika.PlainCredentials(USER, PASSWORD)
        )
    )

    # 2. 创建信道
    ch = conn.channel()

    # 3. 声明 topic 交换机(确保存在)
    ch.exchange_declare(
        exchange=EXCHANGE_NAME,
        exchange_type='topic',
        durable=True
    )

    # 4. 发布消息到指定主题(路由键)
    ch.basic_publish(
        exchange=EXCHANGE_NAME,
        routing_key=topic,
        body=json.dumps(message),
        properties=pika.BasicProperties(delivery_mode=2)  # 持久化消息
    )

    print(f"✅ 发布到主题 [{topic}]: {message}")
    conn.close()

if __name__ == "__main__":
    # 模拟发布不同主题的消息
    send_to_topic("sensor.temp", {"value": 25.5, "unit": "C"})
    send_to_topic("sensor.humidity", {"value": 60, "unit": "%"})
    send_to_topic("device.control", {"cmd": "restart", "target": "sensor_001"})

3. consumer_topic.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
consumer_topic.py - 只订阅 sensor 开头的主题

功能说明:
1. 连接到 RabbitMQ Broker
2. 绑定队列到 topic 交换机,只接收 sensor 开头的主题
3. 消费消息并手动确认
"""

import pika
import json

# ======================== 配置参数 ========================
HOST = 'localhost'              # RabbitMQ 服务器的 IP 地址
PORT = 5672                     # RabbitMQ 默认 AMQP 端口
USER = 'mi_user'                # 用户名
PASSWORD = 'YourPassword123'    # 密码
EXCHANGE_NAME = 'my_topic_exchange'  # 交换机名称
QUEUE_NAME = 'topic_queue'           # 队列名称

def callback(ch, method, properties, body):
    """
    收到消息的回调函数
    :param ch: 信道对象
    :param method: 消息方法属性(包含 delivery_tag 等)
    :param properties: 消息属性
    :param body: 消息内容(二进制数据)
    """
    topic = method.routing_key  # 获取消息的主题(路由键)
    message = json.loads(body.decode('utf-8'))  # 将二进制数据转换为 Python 字典
    print(f"\n📨 收到主题 [{topic}]: {message}")
    ch.basic_ack(delivery_tag=method.delivery_tag)  # 手动确认消息已处理完成

def start_consumer():
    """
    启动消费者,只订阅 sensor 开头的主题
    """
    # 1. 创建连接
    conn = pika.BlockingConnection(
        pika.ConnectionParameters(
            host=HOST,
            port=PORT,
            credentials=pika.PlainCredentials(USER, PASSWORD)
        )
    )

    # 2. 创建信道
    ch = conn.channel()

    # 3. 声明 topic 交换机(确保存在)
    ch.exchange_declare(
        exchange=EXCHANGE_NAME,
        exchange_type='topic',
        durable=True
    )

    # 4. 声明队列(确保存在)
    ch.queue_declare(queue=QUEUE_NAME, durable=True)

    # 5. 绑定队列到交换机,只接收 sensor 开头的主题
    ch.queue_bind(
        exchange=EXCHANGE_NAME,
        queue=QUEUE_NAME,
        routing_key='sensor.#'  # 绑定键(只匹配 sensor 开头的主题)
    )

    # 6. 设置每次只处理一条消息(公平分发)
    ch.basic_qos(prefetch_count=1)

    # 7. 开始消费消息
    ch.basic_consume(queue=QUEUE_NAME, on_message_callback=callback, auto_ack=False)

    print(f"🔄 开始订阅 sensor 开头的主题 (按 Ctrl+C 退出)")
    ch.start_consuming()

if __name__ == "__main__":
    start_consumer()
相关推荐
Rabbit_QL9 小时前
【水印添加工具】从零设计一个工程级 Python 图片水印工具:WaterMask 架构与实现
开发语言·python
曲幽10 小时前
FastAPI多进程部署:定时任务重复执行?手把手教你用锁搞定
redis·python·fastapi·web·lock·works
森屿~~11 小时前
AI 手势识别系统:踩坑与实现全记录 (PyTorch + MediaPipe)
人工智能·pytorch·python
忧郁的橙子.11 小时前
26期_01_Pyhton文件的操作
开发语言·python
小CC吃豆子12 小时前
Python爬虫
开发语言·python
June bug13 小时前
(#字符串处理)字符串中第一个不重复的字母
python·leetcode·面试·职场和发展·跳槽
lixzest13 小时前
PyTorch基础知识简述
人工智能·pytorch·python
飞Link13 小时前
深度学习里程碑:ResNet(残差网络)从理论到实战全解析
人工智能·python·深度学习
ASS-ASH14 小时前
霸王色霸气的本质概括分析
人工智能·python·机器学习·大脑·脑电波
ValidationExpression14 小时前
学习:词嵌入(Word Embedding / Text Embedding)技术
python·学习·ai