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())