RabbitMQ消息队列实战项目

python 复制代码
RabbitMQ消息队列实战项目
项目一:电商订单处理系统
项目架构
text
[Web应用] → [订单队列] → [订单处理服务] → [库存队列] → [库存服务]
                ↓                           ↓
          [死信队列]                    [通知队列] → [短信/邮件服务]
核心代码实现
1. 环境配置
python
# config.py
import pika
import json
from typing import Dict, Any

class RabbitMQConfig:
    # 连接配置
    RABBITMQ_HOST = 'localhost'
    RABBITMQ_PORT = 5672
    RABBITMQ_USER = 'guest'
    RABBITMQ_PASSWORD = 'guest'
    VIRTUAL_HOST = '/'
    
    # 交换机配置
    ORDER_EXCHANGE = 'order.exchange'
    INVENTORY_EXCHANGE = 'inventory.exchange'
    NOTIFICATION_EXCHANGE = 'notification.exchange'
    
    # 队列配置
    ORDER_QUEUE = 'order.queue'
    ORDER_DLX_QUEUE = 'order.dlx.queue'
    INVENTORY_QUEUE = 'inventory.queue'
    EMAIL_QUEUE = 'email.queue'
    SMS_QUEUE = 'sms.queue'
    
    # 路由键
    ORDER_ROUTING_KEY = 'order.create'
    INVENTORY_ROUTING_KEY = 'inventory.check'
    EMAIL_ROUTING_KEY = 'notification.email'
    SMS_ROUTING_KEY = 'notification.sms'
2. 连接管理器
python
# connection_manager.py
import pika
from typing import Optional
import threading
import logging

class RabbitMQConnectionManager:
    """RabbitMQ连接管理器(单例模式)"""
    
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
                cls._instance._initialized = False
            return cls._instance
    
    def __init__(self):
        if self._initialized:
            return
            
        self._initialized = True
        self.connection: Optional[pika.BlockingConnection] = None
        self.channel: Optional[pika.adapters.blocking_connection.BlockingChannel] = None
        self.logger = logging.getLogger(__name__)
        
    def connect(self):
        """建立连接"""
        try:
            credentials = pika.PlainCredentials(
                RabbitMQConfig.RABBITMQ_USER,
                RabbitMQConfig.RABBITMQ_PASSWORD
            )
            
            parameters = pika.ConnectionParameters(
                host=RabbitMQConfig.RABBITMQ_HOST,
                port=RabbitMQConfig.RABBITMQ_PORT,
                virtual_host=RabbitMQConfig.VIRTUAL_HOST,
                credentials=credentials,
                heartbeat=600,
                blocked_connection_timeout=300
            )
            
            self.connection = pika.BlockingConnection(parameters)
            self.channel = self.connection.channel()
            self.logger.info("RabbitMQ连接成功")
            
        except Exception as e:
            self.logger.error(f"RabbitMQ连接失败: {e}")
            raise
    
    def get_channel(self):
        """获取channel"""
        if not self.connection or self.connection.is_closed:
            self.connect()
        return self.channel
    
    def close(self):
        """关闭连接"""
        if self.connection and not self.connection.is_closed:
            self.connection.close()
            self.logger.info("RabbitMQ连接已关闭")
3. 消息发布者
python
# publisher.py
import json
import uuid
from datetime import datetime
from typing import Dict, Any
import pika

class OrderPublisher:
    """订单消息发布者"""
    
    def __init__(self, connection_manager):
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        self.setup_exchanges()
    
    def setup_exchanges(self):
        """设置交换机"""
        # 订单交换机
        self.channel.exchange_declare(
            exchange=RabbitMQConfig.ORDER_EXCHANGE,
            exchange_type='direct',
            durable=True
        )
        
        # 死信交换机
        self.channel.exchange_declare(
            exchange='order.dlx.exchange',
            exchange_type='direct',
            durable=True
        )
        
        # 队列声明
        arguments = {
            'x-dead-letter-exchange': 'order.dlx.exchange',
            'x-dead-letter-routing-key': 'order.dlx',
            'x-message-ttl': 30000  # 30秒过期
        }
        
        self.channel.queue_declare(
            queue=RabbitMQConfig.ORDER_QUEUE,
            durable=True,
            arguments=arguments
        )
        
        # 死信队列
        self.channel.queue_declare(
            queue=RabbitMQConfig.ORDER_DLX_QUEUE,
            durable=True
        )
        
        # 绑定
        self.channel.queue_bind(
            exchange=RabbitMQConfig.ORDER_EXCHANGE,
            queue=RabbitMQConfig.ORDER_QUEUE,
            routing_key=RabbitMQConfig.ORDER_ROUTING_KEY
        )
        
        self.channel.queue_bind(
            exchange='order.dlx.exchange',
            queue=RabbitMQConfig.ORDER_DLX_QUEUE,
            routing_key='order.dlx'
        )
    
    def publish_order(self, order_data: Dict[str, Any]):
        """发布订单消息"""
        message_id = str(uuid.uuid4())
        message = {
            'message_id': message_id,
            'timestamp': datetime.now().isoformat(),
            'data': order_data
        }
        
        # 消息属性
        properties = pika.BasicProperties(
            delivery_mode=2,  # 持久化
            message_id=message_id,
            content_type='application/json',
            timestamp=int(datetime.now().timestamp())
        )
        
        try:
            self.channel.basic_publish(
                exchange=RabbitMQConfig.ORDER_EXCHANGE,
                routing_key=RabbitMQConfig.ORDER_ROUTING_KEY,
                body=json.dumps(message),
                properties=properties
            )
            print(f"订单消息已发布: {message_id}")
            return message_id
            
        except Exception as e:
            print(f"消息发布失败: {e}")
            raise
4. 消息消费者
python
# consumer.py
import json
import time
from typing import Callable
import pika

class OrderConsumer:
    """订单消息消费者"""
    
    def __init__(self, connection_manager):
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        self.setup_qos()
    
    def setup_qos(self):
        """设置QoS"""
        # 每次只处理一条消息
        self.channel.basic_qos(prefetch_count=1)
    
    def process_order(self, ch, method, properties, body):
        """处理订单消息"""
        try:
            message = json.loads(body)
            order_data = message['data']
            
            print(f"处理订单: {order_data['order_id']}")
            
            # 模拟订单处理
            time.sleep(2)
            
            # 处理成功,确认消息
            ch.basic_ack(delivery_tag=method.delivery_tag)
            print(f"订单处理完成: {order_data['order_id']}")
            
        except Exception as e:
            print(f"订单处理失败: {e}")
            
            # 判断是否重新入队
            if method.redelivered:
                # 已经重试过,拒绝消息
                ch.basic_reject(delivery_tag=method.delivery_tag, requeue=False)
            else:
                # 第一次失败,重新入队
                ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
    
    def start_consuming(self):
        """开始消费"""
        self.channel.basic_consume(
            queue=RabbitMQConfig.ORDER_QUEUE,
            on_message_callback=self.process_order,
            auto_ack=False
        )
        
        print("开始监听订单队列...")
        self.channel.start_consuming()
5. 异步消息处理
python
# async_processor.py
import asyncio
import aio_pika
from typing import Dict, Any
import json

class AsyncOrderProcessor:
    """异步订单处理器"""
    
    def __init__(self, rabbitmq_url: str):
        self.rabbitmq_url = rabbitmq_url
        self.connection = None
        self.channel = None
    
    async def connect(self):
        """建立异步连接"""
        self.connection = await aio_pika.connect_robust(
            self.rabbitmq_url,
            reconnect_interval=5
        )
        self.channel = await self.connection.channel()
        
        # 设置QoS
        await self.channel.set_qos(prefetch_count=10)
    
    async def process_order(self, message: aio_pika.IncomingMessage):
        """处理订单"""
        async with message.process():
            try:
                order_data = json.loads(message.body)
                print(f"异步处理订单: {order_data['order_id']}")
                
                # 模拟异步处理
                await asyncio.sleep(1)
                
                # 调用其他服务
                await self.check_inventory(order_data)
                await self.send_notification(order_data)
                
                print(f"订单处理完成: {order_data['order_id']}")
                
            except Exception as e:
                print(f"异步处理失败: {e}")
                # 消息会被自动拒绝并重新入队
                raise
    
    async def check_inventory(self, order_data: Dict[str, Any]):
        """检查库存"""
        # 发送库存检查消息
        inventory_exchange = await self.channel.declare_exchange(
            RabbitMQConfig.INVENTORY_EXCHANGE,
            aio_pika.ExchangeType.DIRECT
        )
        
        await inventory_exchange.publish(
            aio_pika.Message(
                body=json.dumps(order_data).encode(),
                delivery_mode=aio_pika.DeliveryMode.PERSISTENT
            ),
            routing_key=RabbitMQConfig.INVENTORY_ROUTING_KEY
        )
    
    async def send_notification(self, order_data: Dict[str, Any]):
        """发送通知"""
        notification_exchange = await self.channel.declare_exchange(
            RabbitMQConfig.NOTIFICATION_EXCHANGE,
            aio_pika.ExchangeType.FANOUT
        )
        
        await notification_exchange.publish(
            aio_pika.Message(
                body=json.dumps({
                    'type': 'order_confirmation',
                    'data': order_data
                }).encode()
            ),
            routing_key=''
        )
    
    async def start(self):
        """启动消费者"""
        await self.connect()
        
        queue = await self.channel.declare_queue(
            RabbitMQConfig.ORDER_QUEUE,
            durable=True
        )
        
        await queue.consume(self.process_order)
        print("异步订单处理器已启动...")
项目二:实时日志监控系统
系统架构
text
[应用服务] → [日志队列] → [日志处理器] → [Elasticsearch]
                              ↓
                        [告警队列] → [告警服务] → [钉钉/邮件]
日志收集器实现
python
# log_collector.py
import logging
import json
import socket
from datetime import datetime
import pika

class RabbitMQLogHandler(logging.Handler):
    """RabbitMQ日志处理器"""
    
    def __init__(self, connection_manager, exchange_name='logs.exchange'):
        super().__init__()
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        self.exchange_name = exchange_name
        self.hostname = socket.gethostname()
        
        # 声明交换机
        self.channel.exchange_declare(
            exchange=exchange_name,
            exchange_type='topic',
            durable=True
        )
    
    def emit(self, record):
        """发送日志"""
        try:
            log_entry = {
                'timestamp': datetime.utcnow().isoformat(),
                'hostname': self.hostname,
                'level': record.levelname,
                'logger': record.name,
                'message': self.format(record),
                'module': record.module,
                'line': record.lineno
            }
            
            # 根据日志级别设置路由键
            routing_key = f"log.{record.levelname.lower()}"
            
            self.channel.basic_publish(
                exchange=self.exchange_name,
                routing_key=routing_key,
                body=json.dumps(log_entry),
                properties=pika.BasicProperties(
                    delivery_mode=1,  # 非持久化
                    content_type='application/json'
                )
            )
            
        except Exception:
            self.handleError(record)


class LogProcessor:
    """日志处理器"""
    
    def __init__(self, connection_manager):
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        self.error_count = 0
        self.setup_queues()
    
    def setup_queues(self):
        """设置队列"""
        self.channel.exchange_declare(
            exchange='logs.exchange',
            exchange_type='topic',
            durable=True
        )
        
        # 错误日志队列
        self.channel.queue_declare(queue='logs.error', durable=True)
        self.channel.queue_bind(
            exchange='logs.exchange',
            queue='logs.error',
            routing_key='log.error'
        )
        
        # 警告日志队列
        self.channel.queue_declare(queue='logs.warning', durable=True)
        self.channel.queue_bind(
            exchange='logs.exchange',
            queue='logs.warning',
            routing_key='log.warning'
        )
        
        # 所有日志队列(用于存储)
        self.channel.queue_declare(queue='logs.all', durable=True)
        self.channel.queue_bind(
            exchange='logs.exchange',
            queue='logs.all',
            routing_key='log.*'
        )
    
    def process_error_log(self, ch, method, properties, body):
        """处理错误日志"""
        log_data = json.loads(body)
        self.error_count += 1
        
        print(f"错误日志: {log_data['message']}")
        
        # 触发告警
        if self.error_count >= 5:  # 5个错误触发告警
            self.send_alert(log_data)
            self.error_count = 0
        
        ch.basic_ack(delivery_tag=method.delivery_tag)
    
    def send_alert(self, error_log):
        """发送告警"""
        alert_data = {
            'type': 'error_alert',
            'message': f"连续错误超过阈值: {error_log['message']}",
            'timestamp': datetime.now().isoformat()
        }
        
        self.channel.basic_publish(
            exchange='alert.exchange',
            routing_key='alert.high',
            body=json.dumps(alert_data)
        )
项目三:任务队列系统(Celery集成)
Celery配置
python
# celery_app.py
from celery import Celery
from kombu import Exchange, Queue

app = Celery(
    'tasks',
    broker='amqp://guest:guest@localhost:5672//',
    backend='redis://localhost:6379/0'
)

# 配置队列
app.conf.task_queues = (
    Queue('high_priority', Exchange('tasks', type='direct'), routing_key='high'),
    Queue('default', Exchange('tasks', type='direct'), routing_key='default'),
    Queue('low_priority', Exchange('tasks', type='direct'), routing_key='low'),
)

app.conf.task_routes = {
    'tasks.process_image': {'queue': 'high_priority'},
    'tasks.send_email': {'queue': 'default'},
    'tasks.generate_report': {'queue': 'low_priority'},
}

app.conf.task_default_queue = 'default'
app.conf.task_default_exchange = 'tasks'
app.conf.task_default_routing_key = 'default'

# 任务实现
@app.task(bind=True, max_retries=3, default_retry_delay=60)
def process_image(self, image_path):
    """图像处理任务"""
    try:
        # 图像处理逻辑
        result = image_processing(image_path)
        return result
    except Exception as exc:
        raise self.retry(exc=exc)

@app.task
def send_email(to_address, subject, body):
    """发送邮件任务"""
    # 邮件发送逻辑
    email_service.send(to_address, subject, body)

@app.task
def generate_report(report_type, params):
    """生成报告任务"""
    # 报告生成逻辑
    return report_service.generate(report_type, params)
项目四:消息广播系统
实现发布/订阅模式
python
# pubsub_system.py
import pika
import json
from typing import List

class MessageBroadcaster:
    """消息广播系统"""
    
    def __init__(self, connection_manager):
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        
    def setup_fanout_exchange(self, exchange_name):
        """设置广播交换机"""
        self.channel.exchange_declare(
            exchange=exchange_name,
            exchange_type='fanout',
            durable=True
        )
    
    def publish_broadcast(self, exchange_name, message):
        """发布广播消息"""
        self.channel.basic_publish(
            exchange=exchange_name,
            routing_key='',  # fanout忽略routing key
            body=json.dumps(message),
            properties=pika.BasicProperties(
                delivery_mode=2,
                content_type='application/json'
            )
        )
    
    def subscribe(self, exchange_name, queue_name='', callback=None):
        """订阅广播消息"""
        # 创建临时队列
        if not queue_name:
            result = self.channel.queue_declare(queue='', exclusive=True)
            queue_name = result.method.queue
        else:
            self.channel.queue_declare(queue=queue_name, durable=True)
        
        # 绑定到广播交换机
        self.channel.queue_bind(
            exchange=exchange_name,
            queue=queue_name
        )
        
        if callback:
            self.channel.basic_consume(
                queue=queue_name,
                on_message_callback=callback,
                auto_ack=True
            )

# WebSocket集成示例
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse

app = FastAPI()

class WebSocketBroadcaster:
    def __init__(self):
        self.connections: List[WebSocket] = []
        self.broadcaster = MessageBroadcaster(RabbitMQConnectionManager())
    
    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.connections.append(websocket)
    
    def disconnect(self, websocket: WebSocket):
        self.connections.remove(websocket)
    
    async def broadcast_to_websockets(self, message):
        for connection in self.connections:
            try:
                await connection.send_text(message)
            except:
                await self.disconnect(connection)
    
    def on_rabbitmq_message(self, ch, method, properties, body):
        """处理RabbitMQ消息并推送到WebSocket"""
        message = body.decode()
        asyncio.create_task(self.broadcast_to_websockets(message))
监控和管理
健康检查与监控
python
# monitoring.py
import psutil
import time
from typing import Dict, Any
import pika

class RabbitMQMonitor:
    """RabbitMQ监控器"""
    
    def __init__(self, connection_manager):
        self.connection_manager = connection_manager
        self.channel = connection_manager.get_channel()
        self.metrics = {}
    
    def get_queue_metrics(self, queue_name: str) -> Dict[str, Any]:
        """获取队列指标"""
        queue = self.channel.queue_declare(
            queue=queue_name,
            passive=True  # 不创建队列
        )
        
        return {
            'queue_name': queue_name,
            'message_count': queue.method.message_count,
            'consumer_count': queue.method.consumer_count
        }
    
    def check_health(self) -> Dict[str, Any]:
        """健康检查"""
        try:
            # 检查连接
            if not self.connection_manager.connection.is_open:
                return {'status': 'unhealthy', 'reason': 'connection_closed'}
            
            # 检查通道
            if not self.channel.is_open:
                return {'status': 'unhealthy', 'reason': 'channel_closed'}
            
            # 检查系统资源
            cpu_usage = psutil.cpu_percent()
            memory_usage = psutil.virtual_memory().percent
            
            if cpu_usage > 90 or memory_usage > 90:
                return {
                    'status': 'degraded',
                    'cpu_usage': cpu_usage,
                    'memory_usage': memory_usage
                }
            
            return {
                'status': 'healthy',
                'cpu_usage': cpu_usage,
                'memory_usage': memory_usage,
                'uptime': time.time() - self.start_time
            }
            
        except Exception as e:
            return {'status': 'unhealthy', 'reason': str(e)}
    
    def collect_metrics(self):
        """收集指标"""
        queues = ['order.queue', 'inventory.queue', 'notification.queue']
        
        for queue in queues:
            try:
                metrics = self.get_queue_metrics(queue)
                self.metrics[queue] = metrics
            except Exception as e:
                self.metrics[queue] = {'error': str(e)}
        
        return self.metrics
这些实战项目涵盖了RabbitMQ的核心使用场景,包括:
1.	订单处理系统的异步解耦
2.	日志收集和监控
3.	任务队列的优先级处理
4.	消息广播和实时推送
5.	系统监控和健康检查
相关推荐
血小板要健康1 小时前
队列 + 宽搜(BFS):二叉树层序遍历 算法总结
java·数据结构·笔记·算法·leetcode·宽度优先
Felven2 小时前
B. Deja Vu
数据结构·算法
不会就选b2 小时前
算法日常・每日刷题--<贪心>3
数据结构·算法·leetcode
ysu_03142 小时前
03-双链表与循环链表
c语言·数据结构·链表
wabs6664 小时前
关于二叉树【力扣107.二叉树的层序遍历II的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
小七在进步4 小时前
数据结构:非比较排序:计数排序
数据结构
一木 之林5 小时前
四、STL 容器与数据结构(进阶)(二)
数据结构·c++·哈希算法
ysu_03145 小时前
02-单链表完全指南
c语言·数据结构·算法·leetcode
Lyyaoo.5 小时前
【链表】【中等】两数相加/倒N删除/两个交换/排序链表/LRU缓存
数据结构·链表·缓存