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单体应用拆分为微服务架构,构建出更加灵活、可扩展和可维护的现代化应用系统。

相关推荐
cup112 小时前
[Full Clock 技术复盘] 一、浏览器前端如何实现百毫秒级时间校准?时间 API 推荐、模拟 NTP 算法原理及局限
typescript·开源·api·时钟·时间同步
_codemonster4 小时前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
Cosolar4 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
qcx236 小时前
【系统学AI】09 Multi-Agent架构(2026版):从学术理论到工业级实践
java·人工智能·架构·multi-agent·claude agent
wb043072017 小时前
厨房质检员——从阿明的“祖传配方“到标准化质检,看测试金字塔的落地
架构·log4j
Dongwoo Jeong7 小时前
微服务架构(MSA)是如何诞生的?
微服务·云原生·架构
半旧夜夏7 小时前
【保姆级】微服务组件环境搭建(Docker Compose版)
java·linux·spring cloud·微服务·云原生·容器
张忠琳8 小时前
【kubernetes v1.21】(kubelet 1)Kubelet 核心架构与启动流程
云原生·架构·kubernetes·kubelet
用户987409238878 小时前
超算中心 高性能计算 htc命令module use的作用
架构
AI科技星9 小时前
基于**v=c(空间光速螺旋运动)唯一第一性原理**重新完整求导证明
人工智能·线性代数·算法·机器学习·架构·概率论·学习方法