Django微服务架构:单体应用拆分解耦实践

目录

  • Django微服务架构:单体应用拆分解耦实践
    • [1. 微服务架构概述](#1. 微服务架构概述)
      • [1.1 什么是微服务架构](#1.1 什么是微服务架构)
      • [1.2 微服务 vs 单体架构](#1.2 微服务 vs 单体架构)
      • [1.3 何时考虑微服务](#1.3 何时考虑微服务)
    • [2. 单体Django应用分析](#2. 单体Django应用分析)
      • [2.1 示例单体应用结构](#2.1 示例单体应用结构)
      • [2.2 识别问题点](#2.2 识别问题点)
      • [2.3 依赖关系分析](#2.3 依赖关系分析)
    • [3. 微服务拆分策略](#3. 微服务拆分策略)
      • [3.1 基于业务能力拆分](#3.1 基于业务能力拆分)
      • [3.2 数据库拆分策略](#3.2 数据库拆分策略)
      • [3.3 服务依赖规划](#3.3 服务依赖规划)
    • [4. 微服务基础设施搭建](#4. 微服务基础设施搭建)
      • [4.1 API网关设计](#4.1 API网关设计)
      • [4.2 服务发现与配置中心](#4.2 服务发现与配置中心)
      • [4.3 消息队列配置](#4.3 消息队列配置)
    • [5. 用户微服务实现](#5. 用户微服务实现)
      • [5.1 用户服务模型](#5.1 用户服务模型)
      • [5.2 用户服务API](#5.2 用户服务API)
      • [5.3 用户服务序列化器](#5.3 用户服务序列化器)
    • [6. 商品微服务实现](#6. 商品微服务实现)
      • [6.1 商品服务模型](#6.1 商品服务模型)
      • [6.2 商品搜索实现](#6.2 商品搜索实现)
    • [7. 订单微服务实现](#7. 订单微服务实现)
      • [7.1 订单服务模型](#7.1 订单服务模型)
      • [7.2 订单服务业务逻辑](#7.2 订单服务业务逻辑)
    • [8. 服务间通信](#8. 服务间通信)
      • [8.1 同步通信(REST API)](#8.1 同步通信(REST API))
      • [8.2 异步通信(消息队列)](#8.2 异步通信(消息队列))
    • [9. 数据一致性解决方案](#9. 数据一致性解决方案)
      • [9.1 Saga模式实现](#9.1 Saga模式实现)
      • [9.2 分布式事务管理](#9.2 分布式事务管理)
    • [10. 监控与运维](#10. 监控与运维)
      • [10.1 健康检查](#10.1 健康检查)
      • [10.2 日志与追踪](#10.2 日志与追踪)
    • [11. 部署与配置管理](#11. 部署与配置管理)
      • [11.1 Docker配置](#11.1 Docker配置)
      • [11.2 配置管理](#11.2 配置管理)
    • [12. 迁移策略与最佳实践](#12. 迁移策略与最佳实践)
      • [12.1 渐进式迁移策略](#12.1 渐进式迁移策略)
      • [12.2 监控和回滚计划](#12.2 监控和回滚计划)
    • [13. 总结](#13. 总结)
      • [13.1 关键收获](#13.1 关键收获)
      • [13.2 成功要素](#13.2 成功要素)
      • [13.3 持续优化方向](#13.3 持续优化方向)

『宝藏代码胶囊开张啦!』------ 我的 CodeCapsule 来咯!✨写代码不再头疼!我的新站点 CodeCapsule 主打一个 "白菜价"+"量身定制 "!无论是卡脖子的毕设/课设/文献复现 ,需要灵光一现的算法改进 ,还是想给项目加个"外挂",这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网

Django微服务架构:单体应用拆分解耦实践

1. 微服务架构概述

1.1 什么是微服务架构

微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间采用轻量级通信机制(通常是HTTP RESTful API)。这些服务围绕业务能力构建,并可以独立部署。

1.2 微服务 vs 单体架构

特性 单体架构 微服务架构
开发速度 初期快,后期慢 初期慢,持续快
部署 整体部署 独立部署
技术栈 统一技术栈 混合技术栈
扩展性 整体扩展 细粒度扩展
故障隔离 单点故障 故障隔离
数据一致性 ACID事务 最终一致性

1.3 何时考虑微服务

  • 团队规模扩大,需要独立开发部署
  • 系统复杂度增加,单体难以维护
  • 需要不同的技术栈应对不同场景
  • 要求高可用性和弹性扩展

2. 单体Django应用分析

2.1 示例单体应用结构

假设我们有一个电商平台单体应用:

复制代码
monolithic_shop/
├── manage.py
├── requirements.txt
├── shop/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
│   └── apps/
│       ├── users/
│       ├── products/
│       ├── orders/
│       ├── payments/
│       ├── inventory/
│       └── notifications/

2.2 识别问题点

python 复制代码
# shop/settings.py 示例 - 展示单体配置的复杂性
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # 业务应用
    'shop.apps.users',
    'shop.apps.products', 
    'shop.apps.orders',
    'shop.apps.payments',
    'shop.apps.inventory',
    'shop.apps.notifications',
    
    # 第三方应用
    'rest_framework',
    'celery',
    'redis',
    # ... 更多依赖
]

# 数据库配置 - 所有应用共享一个数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'monolithic_shop',
        'USER': 'postgres',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Celery配置 - 所有异步任务集中处理
CELERY_BROKER_URL = 'redis://localhost:6379/0'

2.3 依赖关系分析

用户服务 商品服务 库存服务 订单服务 支付服务 通知服务

3. 微服务拆分策略

3.1 基于业务能力拆分

python 复制代码
# 识别核心业务域
BUSINESS_DOMAINS = {
    'user_management': ['认证', '授权', '用户档案', '权限管理'],
    'product_catalog': ['商品管理', '分类管理', '搜索', '评价'],
    'order_processing': ['购物车', '订单', '配送', '退货'],
    'payment_processing': ['支付', '退款', '对账', '风控'],
    'inventory_management': ['库存', '仓储', '物流', '供应链'],
    'notification_service': ['邮件', '短信', '推送', '消息队列'],
}

3.2 数据库拆分策略

python 复制代码
# 单体数据库表关系分析
DATABASE_TABLES = {
    'users': ['auth_user', 'auth_group', 'user_profile', 'user_address'],
    'products': ['product', 'category', 'product_image', 'product_review'],
    'orders': ['order', 'order_item', 'shipping_info', 'return_request'],
    'payments': ['payment', 'refund', 'transaction', 'payment_method'],
    'inventory': ['stock', 'warehouse', 'supplier', 'inventory_log'],
    'notifications': ['notification', 'email_template', 'sms_log', 'push_token'],
}

3.3 服务依赖规划

python 复制代码
# 服务间依赖关系
SERVICE_DEPENDENCIES = {
    'order_service': ['user_service', 'product_service', 'inventory_service'],
    'payment_service': ['user_service', 'order_service'],
    'notification_service': ['user_service', 'order_service'],
    'inventory_service': ['product_service'],
}

4. 微服务基础设施搭建

4.1 API网关设计

python 复制代码
# gateway/apps.py
from django.apps import AppConfig

class GatewayConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'gateway'
python 复制代码
# gateway/views.py
import requests
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
import json
import logging

logger = logging.getLogger(__name__)

class ServiceRegistry:
    """服务注册中心"""
    SERVICES = {
        'users': 'http://user-service:8001',
        'products': 'http://product-service:8002', 
        'orders': 'http://order-service:8003',
        'payments': 'http://payment-service:8004',
        'inventory': 'http://inventory-service:8005',
        'notifications': 'http://notification-service:8006',
    }
    
    @classmethod
    def get_service_url(cls, service_name):
        return cls.SERVICES.get(service_name)

@csrf_exempt
def api_gateway(request, service, path):
    """API网关路由"""
    service_url = ServiceRegistry.get_service_url(service)
    
    if not service_url:
        return JsonResponse({'error': 'Service not found'}, status=404)
    
    # 构建目标URL
    target_url = f"{service_url}/api/{path}"
    
    # 转发请求
    try:
        headers = {
            'Content-Type': 'application/json',
            'Authorization': request.headers.get('Authorization', '')
        }
        
        # 提取请求数据
        if request.body:
            data = request.body
        else:
            data = None
        
        # 发送请求
        response = requests.request(
            method=request.method,
            url=target_url,
            headers=headers,
            data=data,
            params=request.GET,
            timeout=30
        )
        
        # 返回响应
        return JsonResponse(
            response.json() if response.content else {},
            status=response.status_code
        )
        
    except requests.exceptions.RequestException as e:
        logger.error(f"Gateway error: {str(e)}")
        return JsonResponse(
            {'error': f'Service unavailable: {service}'}, 
            status=503
        )

class CircuitBreaker:
    """断路器模式实现"""
    def __init__(self, failure_threshold=5, recovery_timeout=60):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.failures = 0
        self.state = 'CLOSED'  # CLOSED, OPEN, HALF_OPEN
        self.last_failure_time = None
    
    def call(self, func, *args, **kwargs):
        if self.state == 'OPEN':
            # 检查是否应该进入半开状态
            if self._should_try_recovery():
                self.state = 'HALF_OPEN'
            else:
                raise Exception("Circuit breaker is OPEN")
        
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure()
            raise e
    
    def _on_success(self):
        self.failures = 0
        self.state = 'CLOSED'
    
    def _on_failure(self):
        self.failures += 1
        self.last_failure_time = time.time()
        
        if self.failures >= self.failure_threshold:
            self.state = 'OPEN'
    
    def _should_try_recovery(self):
        if (self.last_failure_time and 
            time.time() - self.last_failure_time > self.recovery_timeout):
            return True
        return False

4.2 服务发现与配置中心

python 复制代码
# service_discovery/models.py
from django.db import models
import uuid

class Microservice(models.Model):
    SERVICE_STATUS = [
        ('UP', '运行中'),
        ('DOWN', '下线'),
        ('STARTING', '启动中'),
        ('OUT_OF_SERVICE', '停止服务'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100, unique=True)
    description = models.TextField(blank=True)
    base_url = models.URLField()
    health_check_url = models.URLField()
    status = models.CharField(max_length=20, choices=SERVICE_STATUS, default='STARTING')
    version = models.CharField(max_length=20, default='1.0.0')
    last_health_check = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'microservices'
        indexes = [
            models.Index(fields=['name', 'status']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.version})"

class ServiceInstance(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    service = models.ForeignKey(Microservice, on_delete=models.CASCADE, related_name='instances')
    instance_id = models.CharField(max_length=100)
    host = models.GenericIPAddressField()
    port = models.IntegerField()
    metadata = models.JSONField(default=dict, blank=True)
    is_active = models.BooleanField(default=True)
    last_heartbeat = models.DateTimeField(auto_now=True)
    registered_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'service_instances'
        unique_together = ['service', 'instance_id']
    
    def __str__(self):
        return f"{self.service.name} - {self.instance_id}"

4.3 消息队列配置

python 复制代码
# shared/messaging/rabbitmq.py
import pika
import json
import logging
from django.conf import settings

logger = logging.getLogger(__name__)

class RabbitMQClient:
    """RabbitMQ消息队列客户端"""
    
    def __init__(self):
        self.connection = None
        self.channel = None
        self.connect()
    
    def connect(self):
        """连接到RabbitMQ"""
        try:
            self.connection = pika.BlockingConnection(
                pika.ConnectionParameters(
                    host=settings.RABBITMQ_HOST,
                    port=settings.RABBITMQ_PORT,
                    credentials=pika.PlainCredentials(
                        settings.RABBITMQ_USERNAME,
                        settings.RABBITMQ_PASSWORD
                    )
                )
            )
            self.channel = self.connection.channel()
            logger.info("Connected to RabbitMQ successfully")
        except Exception as e:
            logger.error(f"Failed to connect to RabbitMQ: {str(e)}")
            raise
    
    def declare_exchange(self, exchange_name, exchange_type='topic'):
        """声明交换机"""
        self.channel.exchange_declare(
            exchange=exchange_name,
            exchange_type=exchange_type,
            durable=True
        )
    
    def declare_queue(self, queue_name, durable=True):
        """声明队列"""
        return self.channel.queue_declare(
            queue=queue_name,
            durable=durable
        )
    
    def publish_message(self, exchange, routing_key, message):
        """发布消息"""
        try:
            self.channel.basic_publish(
                exchange=exchange,
                routing_key=routing_key,
                body=json.dumps(message),
                properties=pika.BasicProperties(
                    delivery_mode=2,  # 持久化消息
                )
            )
            logger.info(f"Message published to {exchange} with key {routing_key}")
        except Exception as e:
            logger.error(f"Failed to publish message: {str(e)}")
            self.connect()  # 重连
            raise
    
    def consume_messages(self, queue_name, callback):
        """消费消息"""
        self.channel.basic_consume(
            queue=queue_name,
            on_message_callback=callback,
            auto_ack=False
        )
        self.channel.start_consuming()

# 定义消息类型
MESSAGE_TYPES = {
    'USER_CREATED': 'user.created',
    'ORDER_CREATED': 'order.created',
    'PAYMENT_PROCESSED': 'payment.processed',
    'INVENTORY_UPDATED': 'inventory.updated',
    'NOTIFICATION_SENT': 'notification.sent',
}

5. 用户微服务实现

5.1 用户服务模型

python 复制代码
# user_service/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
import uuid

class User(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    email = models.EmailField(unique=True)
    phone = models.CharField(max_length=20, blank=True)
    avatar = models.URLField(blank=True)
    date_of_birth = models.DateField(null=True, blank=True)
    is_verified = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'users'
    
    def __str__(self):
        return self.email

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.TextField(max_length=500, blank=True)
    company = models.CharField(max_length=100, blank=True)
    location = models.CharField(max_length=100, blank=True)
    website = models.URLField(blank=True)
    
    class Meta:
        db_table = 'user_profiles'

class Address(models.Model):
    ADDRESS_TYPES = [
        ('HOME', '家庭地址'),
        ('WORK', '工作地址'),
        ('BILLING', '账单地址'),
        ('SHIPPING', '配送地址'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='addresses')
    address_type = models.CharField(max_length=10, choices=ADDRESS_TYPES)
    recipient_name = models.CharField(max_length=100)
    phone = models.CharField(max_length=20)
    address_line1 = models.CharField(max_length=255)
    address_line2 = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100)
    postal_code = models.CharField(max_length=20)
    country = models.CharField(max_length=100, default='中国')
    is_default = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'user_addresses'
        indexes = [
            models.Index(fields=['user', 'address_type']),
        ]
    
    def __str__(self):
        return f"{self.recipient_name} - {self.city}"

5.2 用户服务API

python 复制代码
# user_service/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from django_filters.rest_framework import DjangoFilterBackend
from .models import User, UserProfile, Address
from .serializers import (
    UserSerializer, UserProfileSerializer, 
    AddressSerializer, UserRegistrationSerializer
)
from .filters import UserFilter, AddressFilter
from shared.messaging.rabbitmq import RabbitMQClient, MESSAGE_TYPES
import logging

logger = logging.getLogger(__name__)

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all().select_related('profile')
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_class = UserFilter
    
    def get_permissions(self):
        if self.action == 'create':
            return [AllowAny()]
        return super().get_permissions()
    
    def get_serializer_class(self):
        if self.action == 'create':
            return UserRegistrationSerializer
        return super().get_serializer_class()
    
    def perform_create(self, serializer):
        user = serializer.save()
        
        # 发送用户创建事件
        try:
            message_client = RabbitMQClient()
            message_client.publish_message(
                exchange='user_events',
                routing_key=MESSAGE_TYPES['USER_CREATED'],
                message={
                    'event_type': 'USER_CREATED',
                    'user_id': str(user.id),
                    'email': user.email,
                    'username': user.username,
                    'timestamp': user.created_at.isoformat()
                }
            )
        except Exception as e:
            logger.error(f"Failed to publish USER_CREATED event: {str(e)}")
    
    @action(detail=False, methods=['get'])
    def me(self, request):
        """获取当前用户信息"""
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)
    
    @action(detail=False, methods=['put'], url_path='me/profile')
    def update_profile(self, request):
        """更新用户资料"""
        profile = request.user.profile
        serializer = UserProfileSerializer(profile, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

class AddressViewSet(viewsets.ModelViewSet):
    serializer_class = AddressSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_class = AddressFilter
    
    def get_queryset(self):
        return Address.objects.filter(user=self.request.user)
    
    def perform_create(self, serializer):
        # 如果设置为默认地址,取消其他默认地址
        if serializer.validated_data.get('is_default', False):
            Address.objects.filter(user=self.request.user, is_default=True).update(is_default=False)
        
        serializer.save(user=self.request.user)

5.3 用户服务序列化器

python 复制代码
# user_service/serializers.py
from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import User, UserProfile, Address

class UserRegistrationSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, validators=[validate_password])
    password_confirm = serializers.CharField(write_only=True)
    
    class Meta:
        model = User
        fields = ('username', 'email', 'password', 'password_confirm', 'first_name', 'last_name')
    
    def validate(self, attrs):
        if attrs['password'] != attrs['password_confirm']:
            raise serializers.ValidationError({"password_confirm": "密码不匹配"})
        return attrs
    
    def create(self, validated_data):
        validated_data.pop('password_confirm')
        user = User.objects.create_user(**validated_data)
        
        # 创建用户资料
        UserProfile.objects.create(user=user)
        
        return user

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = ('bio', 'company', 'location', 'website')

class UserSerializer(serializers.ModelSerializer):
    profile = UserProfileSerializer(read_only=True)
    
    class Meta:
        model = User
        fields = ('id', 'username', 'email', 'first_name', 'last_name', 
                 'phone', 'avatar', 'date_of_birth', 'is_verified', 
                 'created_at', 'profile')
        read_only_fields = ('id', 'created_at', 'is_verified')

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = '__all__'
        read_only_fields = ('id', 'user', 'created_at', 'updated_at')
    
    def validate(self, attrs):
        # 验证地址数据
        if not attrs.get('recipient_name'):
            raise serializers.ValidationError("收件人姓名不能为空")
        return attrs

6. 商品微服务实现

6.1 商品服务模型

python 复制代码
# product_service/models.py
from django.db import models
import uuid

class Category(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    parent = models.ForeignKey('self', on_delete=models.CASCADE, 
                             null=True, blank=True, related_name='children')
    image = models.URLField(blank=True)
    is_active = models.BooleanField(default=True)
    sort_order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'categories'
        verbose_name_plural = 'Categories'
        ordering = ['sort_order', 'name']
    
    def __str__(self):
        return self.name

class Product(models.Model):
    STATUS_CHOICES = [
        ('DRAFT', '草稿'),
        ('ACTIVE', '上架'),
        ('INACTIVE', '下架'),
        ('ARCHIVED', '归档'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    description = models.TextField()
    short_description = models.TextField(max_length=500)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
    price = models.DecimalField(max_digits=10, decimal_places=2)
    compare_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    cost_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    sku = models.CharField(max_length=100, unique=True)
    barcode = models.CharField(max_length=100, blank=True)
    weight = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True)
    dimensions = models.CharField(max_length=100, blank=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='DRAFT')
    is_featured = models.BooleanField(default=False)
    is_digital = models.BooleanField(default=False)
    download_url = models.URLField(blank=True)
    view_count = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'products'
        indexes = [
            models.Index(fields=['status', 'is_featured']),
            models.Index(fields=['category', 'status']),
            models.Index(fields=['sku']),
        ]
        ordering = ['-created_at']
    
    def __str__(self):
        return self.name
    
    @property
    def discount_percentage(self):
        if self.compare_price and self.compare_price > self.price:
            return round(((self.compare_price - self.price) / self.compare_price) * 100)
        return 0

class ProductImage(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
    image = models.URLField()
    alt_text = models.CharField(max_length=200, blank=True)
    is_primary = models.BooleanField(default=False)
    sort_order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'product_images'
        ordering = ['sort_order', 'created_at']

class ProductReview(models.Model):
    RATING_CHOICES = [
        (1, '1星'),
        (2, '2星'),
        (3, '3星'),
        (4, '4星'),
        (5, '5星'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='reviews')
    user_id = models.UUIDField()  # 来自用户服务
    rating = models.IntegerField(choices=RATING_CHOICES)
    title = models.CharField(max_length=200)
    comment = models.TextField()
    is_approved = models.BooleanField(default=False)
    helpful_count = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'product_reviews'
        unique_together = ['product', 'user_id']
        ordering = ['-created_at']
    
    def __str__(self):
        return f"Review for {self.product.name} by {self.user_id}"

6.2 商品搜索实现

python 复制代码
# product_service/search.py
from django_elasticsearch_dsl import Document, fields
from django_elasticsearch_dsl.registries import registry
from .models import Product, Category

@registry.register_document
class ProductDocument(Document):
    category = fields.ObjectField(properties={
        'name': fields.TextField(),
        'description': fields.TextField(),
    })
    
    class Index:
        name = 'products'
        settings = {
            'number_of_shards': 1,
            'number_of_replicas': 0
        }
    
    class Django:
        model = Product
        fields = [
            'id',
            'name',
            'slug',
            'description',
            'short_description',
            'price',
            'status',
            'is_featured',
            'sku',
            'created_at',
            'updated_at',
        ]
        related_models = [Category]
    
    def get_queryset(self):
        return super().get_queryset().select_related('category')
    
    def get_instances_from_related(self, related_instance):
        if isinstance(related_instance, Category):
            return related_instance.products.all()

# product_service/views.py
from elasticsearch_dsl import Q
from .search import ProductDocument

class ProductSearchView(APIView):
    permission_classes = [AllowAny]
    
    def get(self, request):
        query = request.GET.get('q', '')
        category = request.GET.get('category')
        min_price = request.GET.get('min_price')
        max_price = request.GET.get('max_price')
        sort_by = request.GET.get('sort_by', 'relevance')
        
        # 构建搜索查询
        search = ProductDocument.search()
        
        if query:
            search = search.query(
                Q('multi_match', query=query, fields=[
                    'name^3', 'description^2', 'short_description', 'category.name'
                ])
            )
        else:
            search = search.sort('-created_at')
        
        # 添加过滤器
        if category:
            search = search.filter('term', category__name=category)
        
        if min_price or max_price:
            price_range = {}
            if min_price:
                price_range['gte'] = float(min_price)
            if max_price:
                price_range['lte'] = float(max_price)
            search = search.filter('range', price=price_range)
        
        # 排序
        if sort_by == 'price_asc':
            search = search.sort('price')
        elif sort_by == 'price_desc':
            search = search.sort('-price')
        elif sort_by == 'newest':
            search = search.sort('-created_at')
        elif sort_by == 'popular':
            search = search.sort('-view_count')
        
        # 执行搜索
        try:
            response = search.execute()
            results = []
            
            for hit in response:
                results.append({
                    'id': hit.id,
                    'name': hit.name,
                    'slug': hit.slug,
                    'short_description': hit.short_description,
                    'price': float(hit.price),
                    'category': hit.category.name,
                    'image': hit.images[0].image if hit.images else None
                })
            
            return Response({
                'query': query,
                'total': response.hits.total.value,
                'results': results
            })
            
        except Exception as e:
            logger.error(f"Search error: {str(e)}")
            return Response({'error': '搜索服务暂时不可用'}, status=503)

7. 订单微服务实现

7.1 订单服务模型

python 复制代码
# order_service/models.py
from django.db import models
import uuid

class Order(models.Model):
    ORDER_STATUS = [
        ('PENDING', '待支付'),
        ('PAID', '已支付'),
        ('PROCESSING', '处理中'),
        ('SHIPPED', '已发货'),
        ('DELIVERED', '已送达'),
        ('CANCELLED', '已取消'),
        ('REFUNDED', '已退款'),
    ]
    
    PAYMENT_METHODS = [
        ('CREDIT_CARD', '信用卡'),
        ('DEBIT_CARD', '借记卡'),
        ('PAYPAL', 'PayPal'),
        ('ALIPAY', '支付宝'),
        ('WECHAT_PAY', '微信支付'),
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    order_number = models.CharField(max_length=20, unique=True)
    user_id = models.UUIDField()  # 来自用户服务
    status = models.CharField(max_length=20, choices=ORDER_STATUS, default='PENDING')
    payment_method = models.CharField(max_length=20, choices=PAYMENT_METHODS)
    subtotal = models.DecimalField(max_digits=10, decimal_places=2)
    tax_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    shipping_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    discount_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2)
    
    # 配送信息
    shipping_address = models.JSONField()  # 存储配送地址快照
    billing_address = models.JSONField()   # 存储账单地址快照
    
    # 时间戳
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    paid_at = models.DateTimeField(null=True, blank=True)
    shipped_at = models.DateTimeField(null=True, blank=True)
    delivered_at = models.DateTimeField(null=True, blank=True)
    cancelled_at = models.DateTimeField(null=True, blank=True)
    
    class Meta:
        db_table = 'orders'
        indexes = [
            models.Index(fields=['user_id', 'created_at']),
            models.Index(fields=['order_number']),
            models.Index(fields=['status']),
        ]
        ordering = ['-created_at']
    
    def __str__(self):
        return self.order_number
    
    def save(self, *args, **kwargs):
        if not self.order_number:
            self.order_number = self.generate_order_number()
        super().save(*args, **kwargs)
    
    def generate_order_number(self):
        import time
        return f"ORD{int(time.time())}{uuid.uuid4().hex[:6].upper()}"
    
    @property
    def can_be_cancelled(self):
        return self.status in ['PENDING', 'PAID']

class OrderItem(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
    product_id = models.UUIDField()  # 来自商品服务
    product_name = models.CharField(max_length=200)
    product_sku = models.CharField(max_length=100)
    product_price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField()
    total_price = models.DecimalField(max_digits=10, decimal_places=2)
    
    class Meta:
        db_table = 'order_items'
    
    def save(self, *args, **kwargs):
        self.total_price = self.product_price * self.quantity
        super().save(*args, **kwargs)

class Cart(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user_id = models.UUIDField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'carts'
    
    def __str__(self):
        return f"Cart for user {self.user_id}"

class CartItem(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items')
    product_id = models.UUIDField()
    quantity = models.PositiveIntegerField(default=1)
    added_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'cart_items'
        unique_together = ['cart', 'product_id']
    
    def __str__(self):
        return f"{self.quantity} x {self.product_id}"

7.2 订单服务业务逻辑

python 复制代码
# order_service/services.py
from django.db import transaction
from shared.messaging.rabbitmq import RabbitMQClient, MESSAGE_TYPES
from .models import Order, OrderItem, Cart, CartItem
import logging
import requests

logger = logging.getLogger(__name__)

class OrderService:
    """订单服务业务逻辑"""
    
    def __init__(self):
        self.mq_client = RabbitMQClient()
    
    def create_order_from_cart(self, user_id, payment_method, shipping_address, billing_address):
        """从购物车创建订单"""
        with transaction.atomic():
            # 获取购物车
            try:
                cart = Cart.objects.get(user_id=user_id)
                cart_items = cart.items.all()
                
                if not cart_items:
                    raise ValueError("购物车为空")
                
                # 验证库存
                for item in cart_items:
                    if not self._check_inventory(item.product_id, item.quantity):
                        raise ValueError(f"商品 {item.product_id} 库存不足")
                
                # 获取商品信息
                product_details = self._get_product_details([item.product_id for item in cart_items])
                
                # 计算订单金额
                subtotal = sum(
                    product_details[item.product_id]['price'] * item.quantity 
                    for item in cart_items
                )
                total_amount = subtotal  # 简化计算,实际应包括税费、运费等
                
                # 创建订单
                order = Order.objects.create(
                    user_id=user_id,
                    payment_method=payment_method,
                    subtotal=subtotal,
                    total_amount=total_amount,
                    shipping_address=shipping_address,
                    billing_address=billing_address
                )
                
                # 创建订单项
                for item in cart_items:
                    product = product_details[item.product_id]
                    OrderItem.objects.create(
                        order=order,
                        product_id=item.product_id,
                        product_name=product['name'],
                        product_sku=product['sku'],
                        product_price=product['price'],
                        quantity=item.quantity,
                        total_price=product['price'] * item.quantity
                    )
                
                # 清空购物车
                cart.items.all().delete()
                
                # 发送订单创建事件
                self.mq_client.publish_message(
                    exchange='order_events',
                    routing_key=MESSAGE_TYPES['ORDER_CREATED'],
                    message={
                        'event_type': 'ORDER_CREATED',
                        'order_id': str(order.id),
                        'user_id': str(user_id),
                        'total_amount': float(total_amount),
                        'items': [
                            {
                                'product_id': str(item.product_id),
                                'quantity': item.quantity,
                                'price': float(product_details[item.product_id]['price'])
                            }
                            for item in cart_items
                        ]
                    }
                )
                
                return order
                
            except Cart.DoesNotExist:
                raise ValueError("购物车不存在")
    
    def _check_inventory(self, product_id, quantity):
        """检查库存(调用库存服务)"""
        try:
            # 这里应该调用库存服务的API
            # 简化实现,直接返回True
            return True
        except Exception as e:
            logger.error(f"Inventory check failed: {str(e)}")
            return False
    
    def _get_product_details(self, product_ids):
        """获取商品详情(调用商品服务)"""
        try:
            # 这里应该调用商品服务的API
            # 简化实现,返回模拟数据
            return {
                product_id: {
                    'name': f'Product {product_id}',
                    'sku': f'SKU-{product_id}',
                    'price': 100.00
                }
                for product_id in product_ids
            }
        except Exception as e:
            logger.error(f"Product service call failed: {str(e)}")
            raise
    
    def update_order_status(self, order_id, new_status, **kwargs):
        """更新订单状态"""
        with transaction.atomic():
            try:
                order = Order.objects.get(id=order_id)
                old_status = order.status
                order.status = new_status
                
                # 更新时间戳
                if new_status == 'PAID':
                    order.paid_at = kwargs.get('paid_at')
                elif new_status == 'SHIPPED':
                    order.shipped_at = kwargs.get('shipped_at')
                elif new_status == 'DELIVERED':
                    order.delivered_at = kwargs.get('delivered_at')
                elif new_status == 'CANCELLED':
                    order.cancelled_at = kwargs.get('cancelled_at')
                
                order.save()
                
                # 发送状态更新事件
                self.mq_client.publish_message(
                    exchange='order_events',
                    routing_key=f"order.{new_status.lower()}",
                    message={
                        'event_type': f'ORDER_{new_status}',
                        'order_id': str(order_id),
                        'old_status': old_status,
                        'new_status': new_status,
                        'timestamp': order.updated_at.isoformat()
                    }
                )
                
                return order
                
            except Order.DoesNotExist:
                raise ValueError("订单不存在")

8. 服务间通信

8.1 同步通信(REST API)

python 复制代码
# shared/http/client.py
import requests
import logging
from django.conf import settings
from urllib.parse import urljoin

logger = logging.getLogger(__name__)

class ServiceClient:
    """服务间HTTP客户端"""
    
    def __init__(self, service_name, base_url=None):
        self.service_name = service_name
        self.base_url = base_url or self._get_service_url(service_name)
        self.session = requests.Session()
        
        # 设置通用headers
        self.session.headers.update({
            'Content-Type': 'application/json',
            'User-Agent': 'Microservice-Client/1.0'
        })
    
    def _get_service_url(self, service_name):
        """从服务注册中心获取服务地址"""
        # 简化实现,实际应该从服务发现获取
        service_registry = {
            'users': 'http://user-service:8001',
            'products': 'http://product-service:8002',
            'orders': 'http://order-service:8003',
            'payments': 'http://payment-service:8004',
            'inventory': 'http://inventory-service:8005',
        }
        return service_registry.get(service_name)
    
    def get(self, endpoint, params=None, timeout=10):
        """GET请求"""
        url = urljoin(self.base_url, endpoint)
        try:
            response = self.session.get(url, params=params, timeout=timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"HTTP GET failed for {url}: {str(e)}")
            raise
    
    def post(self, endpoint, data=None, timeout=10):
        """POST请求"""
        url = urljoin(self.base_url, endpoint)
        try:
            response = self.session.post(url, json=data, timeout=timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"HTTP POST failed for {url}: {str(e)}")
            raise
    
    def put(self, endpoint, data=None, timeout=10):
        """PUT请求"""
        url = urljoin(self.base_url, endpoint)
        try:
            response = self.session.put(url, json=data, timeout=timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"HTTP PUT failed for {url}: {str(e)}")
            raise
    
    def delete(self, endpoint, timeout=10):
        """DELETE请求"""
        url = urljoin(self.base_url, endpoint)
        try:
            response = self.session.delete(url, timeout=timeout)
            response.raise_for_status()
            return response.json() if response.content else {}
        except requests.exceptions.RequestException as e:
            logger.error(f"HTTP DELETE failed for {url}: {str(e)}")
            raise

# 使用示例
class ProductServiceClient(ServiceClient):
    def __init__(self):
        super().__init__('products')
    
    def get_product(self, product_id):
        return self.get(f'/api/products/{product_id}/')
    
    def get_products_batch(self, product_ids):
        return self.post('/api/products/batch/', {'ids': product_ids})

class UserServiceClient(ServiceClient):
    def __init__(self):
        super().__init__('users')
    
    def get_user(self, user_id):
        return self.get(f'/api/users/{user_id}/')
    
    def validate_token(self, token):
        return self.post('/api/auth/validate/', {'token': token})

8.2 异步通信(消息队列)

python 复制代码
# shared/messaging/event_handlers.py
import json
import logging
from shared.messaging.rabbitmq import RabbitMQClient

logger = logging.getLogger(__name__)

class EventHandler:
    """事件处理器基类"""
    
    def __init__(self):
        self.mq_client = RabbitMQClient()
    
    def handle(self, event_type, payload):
        """处理事件"""
        handler_method = getattr(self, f"handle_{event_type.lower()}", None)
        if handler_method:
            handler_method(payload)
        else:
            logger.warning(f"No handler for event type: {event_type}")

class OrderEventHandler(EventHandler):
    """订单事件处理器"""
    
    def handle_order_created(self, payload):
        """处理订单创建事件"""
        try:
            order_id = payload['order_id']
            user_id = payload['user_id']
            total_amount = payload['total_amount']
            
            logger.info(f"Processing order created event: {order_id}")
            
            # 这里可以触发其他操作,比如:
            # 1. 发送确认邮件
            # 2. 更新库存
            # 3. 触发支付流程
            
        except Exception as e:
            logger.error(f"Failed to handle ORDER_CREATED event: {str(e)}")
    
    def handle_order_paid(self, payload):
        """处理订单支付事件"""
        try:
            order_id = payload['order_id']
            
            logger.info(f"Processing order paid event: {order_id}")
            
            # 订单支付后的处理逻辑
            # 1. 更新订单状态
            # 2. 准备发货
            # 3. 发送支付确认通知
            
        except Exception as e:
            logger.error(f"Failed to handle ORDER_PAID event: {str(e)}")

class UserEventHandler(EventHandler):
    """用户事件处理器"""
    
    def handle_user_created(self, payload):
        """处理用户创建事件"""
        try:
            user_id = payload['user_id']
            email = payload['email']
            
            logger.info(f"Processing user created event: {user_id}")
            
            # 新用户注册后的处理逻辑
            # 1. 发送欢迎邮件
            # 2. 创建用户购物车
            # 3. 初始化用户偏好设置
            
        except Exception as e:
            logger.error(f"Failed to handle USER_CREATED event: {str(e)}")

# 事件消费者
def start_event_consumers():
    """启动事件消费者"""
    mq_client = RabbitMQClient()
    
    # 声明交换机和队列
    mq_client.declare_exchange('user_events', 'topic')
    mq_client.declare_exchange('order_events', 'topic')
    
    # 用户事件队列
    mq_client.declare_queue('user_events_queue')
    mq_client.channel.queue_bind(
        exchange='user_events',
        queue='user_events_queue',
        routing_key='user.*'
    )
    
    # 订单事件队列  
    mq_client.declare_queue('order_events_queue')
    mq_client.channel.queue_bind(
        exchange='order_events',
        queue='order_events_queue',
        routing_key='order.*'
    )
    
    # 启动消费者
    user_handler = UserEventHandler()
    order_handler = OrderEventHandler()
    
    def user_callback(ch, method, properties, body):
        try:
            message = json.loads(body)
            user_handler.handle(message['event_type'], message)
            ch.basic_ack(delivery_tag=method.delivery_tag)
        except Exception as e:
            logger.error(f"User event processing failed: {str(e)}")
            ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
    
    def order_callback(ch, method, properties, body):
        try:
            message = json.loads(body)
            order_handler.handle(message['event_type'], message)
            ch.basic_ack(delivery_tag=method.delivery_tag)
        except Exception as e:
            logger.error(f"Order event processing failed: {str(e)}")
            ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
    
    # 开始消费
    mq_client.channel.basic_consume(
        queue='user_events_queue',
        on_message_callback=user_callback
    )
    
    mq_client.channel.basic_consume(
        queue='order_events_queue',
        on_message_callback=order_callback
    )
    
    logger.info("Event consumers started")

9. 数据一致性解决方案

9.1 Saga模式实现

python 复制代码
# shared/saga/saga_orchestrator.py
import uuid
import logging
from enum import Enum
from typing import List, Dict, Callable

logger = logging.getLogger(__name__)

class SagaStatus(Enum):
    PENDING = "PENDING"
    IN_PROGRESS = "IN_PROGRESS"
    COMPLETED = "COMPLETED"
    FAILED = "FAILED"
    COMPENSATING = "COMPENSATING"

class SagaStep:
    """Saga步骤定义"""
    
    def __init__(self, name: str, execute: Callable, compensate: Callable):
        self.name = name
        self.execute = execute
        self.compensate = compensate

class SagaOrchestrator:
    """Saga编排器"""
    
    def __init__(self):
        self.sagas = {}
    
    def create_saga(self, steps: List[SagaStep]) -> str:
        """创建Saga事务"""
        saga_id = str(uuid.uuid4())
        
        self.sagas[saga_id] = {
            'id': saga_id,
            'steps': steps,
            'current_step': 0,
            'status': SagaStatus.PENDING,
            'execution_log': []
        }
        
        logger.info(f"Created saga: {saga_id}")
        return saga_id
    
    async def execute_saga(self, saga_id: str, initial_data: Dict = None):
        """执行Saga事务"""
        saga = self.sagas.get(saga_id)
        if not saga:
            raise ValueError(f"Saga {saga_id} not found")
        
        saga['status'] = SagaStatus.IN_PROGRESS
        execution_data = initial_data or {}
        
        try:
            for i, step in enumerate(saga['steps']):
                saga['current_step'] = i
                
                logger.info(f"Executing step {i}: {step.name}")
                
                # 执行步骤
                result = await step.execute(execution_data)
                execution_data.update(result)
                
                # 记录执行日志
                saga['execution_log'].append({
                    'step': step.name,
                    'status': 'SUCCESS',
                    'data': result
                })
            
            # 所有步骤执行成功
            saga['status'] = SagaStatus.COMPLETED
            logger.info(f"Saga {saga_id} completed successfully")
            
        except Exception as e:
            logger.error(f"Saga {saga_id} failed at step {saga['current_step']}: {str(e)}")
            await self.compensate_saga(saga_id, execution_data)
            raise
    
    async def compensate_saga(self, saga_id: str, execution_data: Dict):
        """补偿Saga事务"""
        saga = self.sagas.get(saga_id)
        if not saga:
            return
        
        saga['status'] = SagaStatus.COMPENSATING
        
        # 从当前步骤反向执行补偿操作
        for i in range(saga['current_step'], -1, -1):
            step = saga['steps'][i]
            
            try:
                logger.info(f"Compensating step {i}: {step.name}")
                await step.compensate(execution_data)
                
                saga['execution_log'].append({
                    'step': step.name,
                    'status': 'COMPENSATED',
                    'data': execution_data
                })
                
            except Exception as e:
                logger.error(f"Compensation failed for step {step.name}: {str(e)}")
                # 即使补偿失败,继续尝试补偿其他步骤
        
        saga['status'] = SagaStatus.FAILED
        logger.info(f"Saga {saga_id} compensation completed")

# 示例:创建订单Saga
class CreateOrderSaga:
    """创建订单Saga"""
    
    def __init__(self, order_service, inventory_service, payment_service):
        self.order_service = order_service
        self.inventory_service = inventory_service
        self.payment_service = payment_service
    
    def create_saga_steps(self, order_data):
        """定义创建订单的Saga步骤"""
        return [
            SagaStep(
                name="validate_order",
                execute=self.validate_order,
                compensate=self.compensate_validate_order
            ),
            SagaStep(
                name="reserve_inventory", 
                execute=self.reserve_inventory,
                compensate=self.compensate_reserve_inventory
            ),
            SagaStep(
                name="process_payment",
                execute=self.process_payment,
                compensate=self.compensate_payment
            ),
            SagaStep(
                name="confirm_order",
                execute=self.confirm_order,
                compensate=self.compensate_confirm_order
            )
        ]
    
    async def validate_order(self, data):
        """验证订单"""
        # 实现订单验证逻辑
        return {'order_valid': True}
    
    async def reserve_inventory(self, data):
        """预留库存"""
        # 调用库存服务预留库存
        return {'inventory_reserved': True}
    
    async def process_payment(self, data):
        """处理支付"""
        # 调用支付服务处理支付
        return {'payment_processed': True}
    
    async def confirm_order(self, data):
        """确认订单"""
        # 确认订单,更新状态
        return {'order_confirmed': True}
    
    # 补偿方法
    async def compensate_validate_order(self, data):
        """补偿订单验证"""
        pass
    
    async def compensate_reserve_inventory(self, data):
        """补偿库存预留"""
        # 释放预留的库存
        pass
    
    async def compensate_payment(self, data):
        """补偿支付"""
        # 退款操作
        pass
    
    async def compensate_confirm_order(self, data):
        """补偿订单确认"""
        # 取消订单
        pass

9.2 分布式事务管理

python 复制代码
# shared/transaction/outbox_pattern.py
from django.db import models, transaction
import uuid
import json

class OutboxMessage(models.Model):
    """发件箱模式消息表"""
    
    MESSAGE_TYPES = [
        ('ORDER_CREATED', '订单创建'),
        ('USER_CREATED', '用户创建'),
        ('PAYMENT_PROCESSED', '支付处理'),
        # ... 其他消息类型
    ]
    
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    message_type = models.CharField(max_length=50, choices=MESSAGE_TYPES)
    payload = models.JSONField()
    destination = models.CharField(max_length=200)  # 目标服务或主题
    status = models.CharField(
        max_length=20, 
        choices=[
            ('PENDING', '待发送'),
            ('SENT', '已发送'),
            ('FAILED', '发送失败')
        ],
        default='PENDING'
    )
    retry_count = models.IntegerField(default=0)
    max_retries = models.IntegerField(default=3)
    created_at = models.DateTimeField(auto_now_add=True)
    processed_at = models.DateTimeField(null=True, blank=True)
    
    class Meta:
        db_table = 'outbox_messages'
        indexes = [
            models.Index(fields=['status', 'created_at']),
            models.Index(fields=['message_type']),
        ]
    
    def __str__(self):
        return f"{self.message_type} - {self.status}"

class OutboxManager:
    """发件箱管理器"""
    
    @transaction.atomic
    def create_message(self, message_type, payload, destination):
        """创建发件箱消息(在数据库事务中)"""
        message = OutboxMessage.objects.create(
            message_type=message_type,
            payload=payload,
            destination=destination
        )
        return message
    
    def process_pending_messages(self):
        """处理待发送的消息"""
        pending_messages = OutboxMessage.objects.filter(
            status='PENDING',
            retry_count__lt=models.F('max_retries')
        )[:100]  # 每次处理100条
        
        for message in pending_messages:
            try:
                self._send_message(message)
                message.status = 'SENT'
                message.processed_at = timezone.now()
                message.save()
                
            except Exception as e:
                logger.error(f"Failed to send outbox message {message.id}: {str(e)}")
                message.retry_count += 1
                if message.retry_count >= message.max_retries:
                    message.status = 'FAILED'
                message.save()

    def _send_message(self, message):
        """发送消息到消息队列"""
        mq_client = RabbitMQClient()
        
        # 根据目的地发送到不同的交换机
        if message.destination.startswith('user.'):
            exchange = 'user_events'
        elif message.destination.startswith('order.'):
            exchange = 'order_events'
        else:
            exchange = 'default_events'
        
        mq_client.publish_message(
            exchange=exchange,
            routing_key=message.destination,
            message={
                'event_type': message.message_type,
                'payload': message.payload,
                'message_id': str(message.id)
            }
        )

10. 监控与运维

10.1 健康检查

python 复制代码
# shared/health/checks.py
from django.db import connection
from django.core.cache import cache
import requests
import logging

logger = logging.getLogger(__name__)

class HealthCheck:
    """健康检查基类"""
    
    def check(self):
        raise NotImplementedError

class DatabaseHealthCheck(HealthCheck):
    """数据库健康检查"""
    
    def check(self):
        try:
            with connection.cursor() as cursor:
                cursor.execute("SELECT 1")
            return True, "Database is healthy"
        except Exception as e:
            return False, f"Database error: {str(e)}"

class CacheHealthCheck(HealthCheck):
    """缓存健康检查"""
    
    def check(self):
        try:
            cache.set('health_check', 'test', 1)
            value = cache.get('health_check')
            if value == 'test':
                return True, "Cache is healthy"
            else:
                return False, "Cache test failed"
        except Exception as e:
            return False, f"Cache error: {str(e)}"

class ServiceHealthCheck(HealthCheck):
    """依赖服务健康检查"""
    
    def __init__(self, service_name, health_check_url):
        self.service_name = service_name
        self.health_check_url = health_check_url
    
    def check(self):
        try:
            response = requests.get(self.health_check_url, timeout=5)
            if response.status_code == 200:
                return True, f"{self.service_name} is healthy"
            else:
                return False, f"{self.service_name} returned {response.status_code}"
        except Exception as e:
            return False, f"{self.service_name} error: {str(e)}"

class HealthCheckRegistry:
    """健康检查注册表"""
    
    def __init__(self):
        self.checks = []
    
    def register(self, check):
        self.checks.append(check)
    
    def run_checks(self):
        results = {
            'status': 'healthy',
            'checks': {}
        }
        
        for check in self.checks:
            is_healthy, message = check.check()
            check_name = check.__class__.__name__
            
            results['checks'][check_name] = {
                'healthy': is_healthy,
                'message': message
            }
            
            if not is_healthy:
                results['status'] = 'unhealthy'
        
        return results

# 初始化健康检查
def init_health_checks():
    registry = HealthCheckRegistry()
    
    # 注册基础检查
    registry.register(DatabaseHealthCheck())
    registry.register(CacheHealthCheck())
    
    # 注册依赖服务检查
    service_checks = {
        'user_service': 'http://user-service:8001/health/',
        'product_service': 'http://product-service:8002/health/',
        'order_service': 'http://order-service:8003/health/',
    }
    
    for service_name, health_url in service_checks.items():
        registry.register(ServiceHealthCheck(service_name, health_url))
    
    return registry

10.2 日志与追踪

python 复制代码
# shared/logging/middleware.py
import uuid
import time
import logging
from django.utils.deprecation import MiddlewareMixin

logger = logging.getLogger(__name__)

class RequestLoggingMiddleware(MiddlewareMixin):
    """请求日志中间件"""
    
    def process_request(self, request):
        request.request_id = str(uuid.uuid4())
        request.start_time = time.time()
        
        logger.info(f"Request started", extra={
            'request_id': request.request_id,
            'method': request.method,
            'path': request.path,
            'user_agent': request.META.get('HTTP_USER_AGENT', ''),
            'ip_address': self.get_client_ip(request)
        })
    
    def process_response(self, request, response):
        if hasattr(request, 'start_time'):
            duration = time.time() - request.start_time
            
            logger.info(f"Request completed", extra={
                'request_id': request.request_id,
                'method': request.method,
                'path': request.path,
                'status_code': response.status_code,
                'duration': duration
            })
        
        return response
    
    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

class ServiceTracingMiddleware:
    """服务追踪中间件"""
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # 从请求头获取或生成追踪ID
        trace_id = request.headers.get('X-Trace-ID', str(uuid.uuid4()))
        span_id = str(uuid.uuid4())
        
        # 设置追踪上下文
        request.trace_id = trace_id
        request.span_id = span_id
        
        # 添加追踪头到出站请求
        def add_tracing_headers(headers=None):
            headers = headers or {}
            headers.update({
                'X-Trace-ID': trace_id,
                'X-Span-ID': span_id,
                'X-Parent-Span-ID': request.headers.get('X-Span-ID', '')
            })
            return headers
        
        request.add_tracing_headers = add_tracing_headers
        
        response = self.get_response(request)
        
        # 添加追踪头到响应
        response['X-Trace-ID'] = trace_id
        
        return response

11. 部署与配置管理

11.1 Docker配置

dockerfile 复制代码
# user_service/Dockerfile
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    gcc \
    postgresql-dev \
    && rm -rf /var/lib/apt/lists/*

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制项目代码
COPY . .

# 创建非root用户
RUN useradd --create-home --shell /bin/bash app
USER app

# 暴露端口
EXPOSE 8001

# 启动命令
CMD ["gunicorn", "user_service.wsgi:application", "--bind", "0.0.0.0:8001"]
yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  # API网关
  gateway:
    build: ./gateway
    ports:
      - "8000:8000"
    environment:
      - DEBUG=False
      - DATABASE_URL=postgresql://user:password@postgres:5432/gateway
    depends_on:
      - postgres
      - redis
    networks:
      - microservices

  # 用户服务
  user-service:
    build: ./user_service
    ports:
      - "8001:8001"
    environment:
      - SERVICE_NAME=user_service
      - DATABASE_URL=postgresql://user:password@postgres:5432/user_service
      - REDIS_URL=redis://redis:6379/0
      - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
    depends_on:
      - postgres
      - redis
      - rabbitmq
    networks:
      - microservices

  # 商品服务
  product-service:
    build: ./product_service
    ports:
      - "8002:8002"
    environment:
      - SERVICE_NAME=product_service
      - DATABASE_URL=postgresql://user:password@postgres:5432/product_service
      - ELASTICSEARCH_URL=http://elasticsearch:9200
    depends_on:
      - postgres
      - elasticsearch
    networks:
      - microservices

  # 订单服务
  order-service:
    build: ./order_service
    ports:
      - "8003:8003"
    environment:
      - SERVICE_NAME=order_service
      - DATABASE_URL=postgresql://user:password@postgres:5432/order_service
    depends_on:
      - postgres
    networks:
      - microservices

  # 基础设施服务
  postgres:
    image: postgres:13
    environment:
      - POSTGRES_MULTIPLE_DATABASES=gateway,user_service,product_service,order_service
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh
    networks:
      - microservices

  redis:
    image: redis:6-alpine
    networks:
      - microservices

  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"  # 管理界面
    networks:
      - microservices

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.14.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data
    networks:
      - microservices

volumes:
  postgres_data:
  elasticsearch_data:

networks:
  microservices:
    driver: bridge

11.2 配置管理

python 复制代码
# shared/config/settings.py
import os
from django.core.exceptions import ImproperlyConfigured

def get_env_variable(var_name, default=None):
    """获取环境变量"""
    try:
        return os.environ[var_name]
    except KeyError:
        if default is not None:
            return default
        error_msg = f"Set the {var_name} environment variable"
        raise ImproperlyConfigured(error_msg)

class ServiceConfig:
    """服务配置基类"""
    
    def __init__(self, service_name):
        self.service_name = service_name
    
    @property
    def database_url(self):
        return get_env_variable(
            'DATABASE_URL',
            f'postgresql://user:password@localhost:5432/{self.service_name}'
        )
    
    @property
    def redis_url(self):
        return get_env_variable('REDIS_URL', 'redis://localhost:6379/0')
    
    @property
    def rabbitmq_url(self):
        return get_env_variable('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672/')
    
    @property
    def debug(self):
        return get_env_variable('DEBUG', 'False').lower() == 'true'
    
    @property
    def allowed_hosts(self):
        hosts = get_env_variable('ALLOWED_HOSTS', 'localhost,127.0.0.1')
        return hosts.split(',')

# 使用示例
user_config = ServiceConfig('user_service')
print(user_config.database_url)  # postgresql://user:password@localhost:5432/user_service

12. 迁移策略与最佳实践

12.1 渐进式迁移策略

python 复制代码
# migration_strategy.py
class MigrationStrategy:
    """迁移策略"""
    
    def __init__(self, monolithic_app):
        self.monolithic_app = monolithic_app
    
    def strangler_fig_pattern(self):
        """绞杀者模式迁移"""
        steps = [
            {
                'phase': 1,
                'description': '识别边界和提取服务',
                'actions': [
                    '分析数据库表依赖关系',
                    '识别服务边界',
                    '创建第一个微服务(如用户服务)',
                    '实现API网关路由'
                ]
            },
            {
                'phase': 2, 
                'description': '并行运行和流量切换',
                'actions': [
                    '新旧系统并行运行',
                    '逐步将流量切换到新服务',
                    '监控性能和错误率',
                    '修复发现的问题'
                ]
            },
            {
                'phase': 3,
                'description': '完成迁移和清理',
                'actions': [
                    '关闭单体中的对应功能',
                    '清理旧代码和数据库表',
                    '优化微服务架构',
                    '文档更新和团队培训'
                ]
            }
        ]
        return steps
    
    def database_migration_plan(self):
        """数据库迁移计划"""
        return {
            'phase1': {
                'description': '数据库读写分离',
                'actions': [
                    '为每个服务创建独立数据库',
                    '设置数据库复制',
                    '迁移历史数据',
                    '验证数据一致性'
                ]
            },
            'phase2': {
                'description': '双写和验证',
                'actions': [
                    '实现双写逻辑',
                    '对比新旧数据一致性',
                    '逐步切换到新数据库',
                    '监控性能影响'
                ]
            },
            'phase3': {
                'description': '切断旧数据库',
                'actions': [
                    '停止向旧数据库写入',
                    '移除旧数据库依赖',
                    '清理旧数据库',
                    '完成迁移'
                ]
            }
        }

12.2 监控和回滚计划

python 复制代码
# migration/monitoring.py
class MigrationMonitor:
    """迁移监控"""
    
    def __init__(self):
        self.metrics = {}
    
    def track_migration_metrics(self):
        """跟踪迁移指标"""
        metrics = {
            'performance': {
                'response_time': self.measure_response_time(),
                'throughput': self.measure_throughput(),
                'error_rate': self.measure_error_rate()
            },
            'business': {
                'order_completion_rate': self.measure_order_completion(),
                'user_satisfaction': self.measure_user_satisfaction(),
                'revenue_impact': self.measure_revenue_impact()
            },
            'technical': {
                'database_connections': self.count_database_connections(),
                'service_health': self.check_service_health(),
                'resource_utilization': self.measure_resource_usage()
            }
        }
        return metrics
    
    def should_rollback(self, metrics):
        """判断是否需要回滚"""
        rollback_conditions = [
            metrics['performance']['error_rate'] > 0.05,  # 错误率超过5%
            metrics['business']['order_completion_rate'] < 0.95,  # 订单完成率低于95%
            any(not health for health in metrics['technical']['service_health'].values())
        ]
        
        return any(rollback_conditions)
    
    def execute_rollback(self):
        """执行回滚"""
        rollback_steps = [
            '将API网关路由切回单体应用',
            '停止微服务实例',
            '恢复数据库连接',
            '验证系统功能',
            '分析失败原因'
        ]
        
        for step in rollback_steps:
            logger.info(f"Rollback step: {step}")
            # 执行回滚操作
        
        logger.info("Rollback completed successfully")

13. 总结

13.1 关键收获

  1. 清晰的边界定义:基于业务能力划分服务边界
  2. 独立的数据存储:每个服务拥有自己的数据库
  3. 异步通信机制:使用消息队列解耦服务
  4. 容错设计:实现断路器、重试、降级等模式
  5. 监控和可观测性:全面的日志、指标和追踪

13.2 成功要素

  • 渐进式迁移:采用绞杀者模式降低风险
  • 自动化运维:CI/CD、容器化、自动化测试
  • 团队组织:按服务划分团队职责
  • 文化转变:从单体思维转向分布式系统思维

13.3 持续优化方向

  1. 性能优化:缓存策略、数据库优化、异步处理
  2. 安全加固:API安全、数据加密、访问控制
  3. 成本优化:资源利用率、自动扩缩容
  4. 用户体验:响应时间、可用性、功能完整性

通过本文的实践指南,您可以系统地将Django单体应用拆分为微服务架构,构建出更加灵活、可扩展和可维护的现代化应用系统。

相关推荐
豆浆Whisky1 小时前
Go微服务通信优化:从协议选择到性能调优全攻略|Go语言进阶(20)
后端·微服务·go
颜颜yan_1 小时前
基于CANN多Stream异步执行的智能推理管道:突破传统串行瓶颈
运维·架构·stream·昇腾·cann
闲人编程1 小时前
Django缓存策略:Redis、Memcached与数据库缓存对比
数据库·redis·缓存·django·memcached·codecapsule
吴法刚2 小时前
Gemini cli 源码分析之-Agent分析-Agent架构系统分析
架构·agent·ai编程·gemini
拾忆,想起2 小时前
Dubbo服务超时与重试策略配置指南:构建 resilient 微服务架构
服务器·网络·微服务·云原生·架构·dubbo
q***51892 小时前
【语义分割】12个主流算法架构介绍、数据集推荐、总结、挑战和未来发展
算法·架构
车载诊断技术2 小时前
电子电气架构 --- 国外对于EE架构的设计方案(上)
架构·汽车·硬件架构·电子电气架构·整车eea简述
颜颜yan_2 小时前
基于昇腾CANN的智能视频分析系统落地实践
架构·音视频·昇腾
杭州杭州杭州3 小时前
实验3 微服务介绍以及开发环境搭建
微服务·云原生·架构