""" RabbitMQ 死信队列处理模块 """ import asyncio import aio_pika import json import logging from datetime import datetime from typing import Dict, Any from .config import config logger = logging.getLogger(__name__) class DeadLetterConsumer: """死信队列消费者""" def __init__(self): """初始化死信队列消费者""" self.connection = None self.channel = None self.dead_letter_queue = None async def connect(self): """建立连接""" try: connection_config = config.get_connection_config() self.connection = await aio_pika.connect_robust(connection_config['uri']) self.channel = await self.connection.channel() await self.channel.set_qos(prefetch_count=connection_config['prefetch_count']) # 声明死信队列 dead_letter_config = config.get_dead_letter_config() self.dead_letter_queue = await self.channel.declare_queue( dead_letter_config['dead_letter_queue'], durable=True, auto_delete=False ) logger.info("[死信消费者] 已连接") except Exception as e: logger.error(f"[死信消费者] 连接失败: {e}") raise async def process_dead_letter_message(self, message: aio_pika.IncomingMessage): """处理死信消息""" try: # 解析死信消息 dead_letter_data = json.loads(message.body.decode('utf-8')) original_message = dead_letter_data.get('original_message', {}) error_info = dead_letter_data.get('error_info', 'Unknown') message_id = dead_letter_data.get('message_id', 'Unknown') # 打印死信消息信息 logger.error("=" * 50) logger.error("[死信消费者] 收到死信消息:") logger.error(f"[死信消费者] 消息ID: {message_id}") logger.error(f"[死信消费者] 消息内容: {json.dumps(original_message, ensure_ascii=False, indent=2)}") logger.error(f"[死信消费者] 错误原因: {error_info}") logger.error("=" * 50) # 保存到数据库 await self.save_to_database(original_message, error_info, message_id) # 确认死信消息 await message.ack() logger.info(f"[死信消费者] 死信消息 {message_id} 处理完成") except Exception as e: logger.error(f"[死信消费者] 处理死信消息失败: {e}") await message.nack(requeue=False) # 拒绝重新入队,避免一致失败出现的死循环 async def save_to_database(self, original_message: Dict[str, Any], error_info: str, message_id: str): """保存死信消息到数据库""" # 模拟数据库保存操作 await asyncio.sleep(0.5) # 构建数据库记录 db_record = { 'id': message_id, 'original_message': original_message, 'error_info': error_info, 'created_at': datetime.now().isoformat(), 'status': 'failed' } logger.info(f"[死信消费者] 💾 死信消息已保存到数据库: {message_id}") logger.info(f"[死信消费者] 数据库记录: {json.dumps(db_record, ensure_ascii=False, indent=2)}") # 这里可以添加实际的数据库操作 # 例如:await database.insert('dead_letter_messages', db_record) async def start_consuming(self): """开始消费死信消息""" self.consumer_tag = await self.dead_letter_queue.consume(self.process_dead_letter_message) logger.info("[死信消费者] 开始消费死信消息...") # 保持消费者运行 await asyncio.Future() async def stop_consuming(self): """停止消费死信消息""" if self.dead_letter_queue and self.consumer_tag: await self.dead_letter_queue.cancel(self.consumer_tag) logger.info("[死信消费者] 已停止消费死信消息") async def close(self): """关闭连接""" try: await self.stop_consuming() if self.connection: await self.connection.close() logger.info("[死信消费者] 连接已关闭") except Exception as e: logger.error(f"[死信消费者] 关闭连接时出错: {e}") async def __aenter__(self): """异步上下文管理器入口""" await self.connect() return self async def __aexit__(self, exc_type, exc_val, exc_tb): """异步上下文管理器出口""" await self.close()