rabbitmq-test/comsumer/fanout_consumer.py
2025-09-07 10:35:24 +08:00

78 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import aio_pika
from config import RABBITMQ_URI
async def fanout_consumer(queue_name: str, consumer_id: int):
"""
Fanout Exchange 消费者:监听单个队列,接收 Fanout 广播的消息
:param queue_name: 要监听的队列名称(与生产者 setup 中创建的队列一致)
:param consumer_id: 消费者标识,区分不同队列的消费者
"""
# 1. 建立稳健连接(自动重连机制,应对网络波动或 RabbitMQ 重启)
connection = await aio_pika.connect_robust(RABBITMQ_URI)
# 2. 创建通信信道(所有消息操作通过信道执行,减少 TCP 连接开销)
channel = await connection.channel()
# 3. 开启公平调度:确保消费者处理完 1 条消息后再接收下 1 条
# 避免单队列内消息堆积(尤其 Fanout 场景下多队列并行处理需均衡)
await channel.set_qos(prefetch_count=1)
# 4. 声明要监听的队列(与生产者 setup_fanout_exchange 中创建的队列完全一致)
# 若队列未初始化(未执行 setup会报错提醒先完成交换器和队列创建
queue = await channel.declare_queue(
queue_name,
durable=True, # 与生产者一致:队列持久化(重启不丢失)
auto_delete=False # 队列不自动删除(即使无消费者也保留,确保广播消息不丢失)
)
# 5. 定义消息处理逻辑Fanout 场景下,同一消息会被所有队列的消费者接收)
async def on_message_received(message: aio_pika.IncomingMessage):
# async with 上下文:自动确认消息(处理完成后告知 RabbitMQ 删除,避免重复消费)
# 若处理崩溃,消息会重新入队,等待消费者重启后重试
async with message.process():
# 解码消息体(生产者用 utf-8 编码,此处对应解码)
message_content = message.body.decode("utf-8")
# 打印关键信息(清晰展示 Fanout 广播特性:同一消息多队列接收)
print(f"【Fanout 消费者{consumer_id}】收到广播消息:")
print(f" 监听队列:{queue_name}")
print(f" 消息内容:{message_content}")
print(f" 消息持久化状态:{'' if message.delivery_mode == 2 else ''}") # 验证持久化
# print(f" 处理时间:{asyncio.get_running_loop().time():.2f}s\n")
# 模拟业务处理耗时(根据实际场景替换,如日志存储、通知推送等)
await asyncio.sleep(1)
# 6. 启动队列监听:将处理逻辑绑定到队列,持续接收广播消息
await queue.consume(on_message_received)
# 打印启动日志,提示用户消费者就绪
print(f"【Fanout 消费者{consumer_id}】已启动,正在监听队列:{queue_name}\n")
# 7. 保持消费者运行(无限期阻塞,直到手动停止程序)
# 若不阻塞,协程会立即结束,消费者连接会断开
await asyncio.Future()
async def start_all_fanout_consumers(queue_prefix="demo.fanout.queue-", queue_count=3):
"""
启动所有 Fanout 消费者:与生产者 setup 中创建的 3 个队列一一对应
:param queue_prefix: 队列名称前缀(与生产者 queue_name_prefix 一致)
:param queue_count: 队列数量(与生产者中 range(3) 对应,默认 3 个)
"""
# 构建消费者任务列表:为每个队列创建一个独立消费者
consumer_tasks = [
fanout_consumer(
queue_name=f"{queue_prefix}{i}", # 队列名demo.fanout.queue-0/1/2与生产者一致
consumer_id=i + 1 # 消费者标识1/2/3
)
for i in range(queue_count)
]
# 并发启动所有消费者3 个消费者同时运行,互不阻塞)
await asyncio.gather(*consumer_tasks)
if __name__ == "__main__":
# 启动所有 Fanout 消费者(需先执行 setup_fanout_exchange 初始化交换器和队列)
asyncio.run(start_all_fanout_consumers())