目录
- 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 关键收获
- 清晰的边界定义:基于业务能力划分服务边界
- 独立的数据存储:每个服务拥有自己的数据库
- 异步通信机制:使用消息队列解耦服务
- 容错设计:实现断路器、重试、降级等模式
- 监控和可观测性:全面的日志、指标和追踪
13.2 成功要素
- 渐进式迁移:采用绞杀者模式降低风险
- 自动化运维:CI/CD、容器化、自动化测试
- 团队组织:按服务划分团队职责
- 文化转变:从单体思维转向分布式系统思维
13.3 持续优化方向
- 性能优化:缓存策略、数据库优化、异步处理
- 安全加固:API安全、数据加密、访问控制
- 成本优化:资源利用率、自动扩缩容
- 用户体验:响应时间、可用性、功能完整性
通过本文的实践指南,您可以系统地将Django单体应用拆分为微服务架构,构建出更加灵活、可扩展和可维护的现代化应用系统。