系统设计:单体不够用了之后
系统设计我没按教科书顺序做。
这次做系统架构演进,从单体到分布式,。
最初的困境
单体的局限
// 单体应用的结构
/app
/controllers
- user.js
- order.js
- payment.js
/models
- user.js
- order.js
- payment.js
/services
- email.js
- notification.js
/routes.js
问题是:
- 代码耦合严重,改一处影响多处
- 部署麻烦,一个小改动要部署整个应用
- 团队协作困难,多人修改同一代码库
- 扩展性差,整体部署或不部署
# 部署一次要停机 30 分钟
npm run build
pm2 stop myapp
rsync -av dist/ server:/var/www/
pm2 restart myapp
什么时候需要拆分
评估标准
团队规模:
- <5 人:单体就好
- 5-20 人:考虑拆分
20 人:必须拆分
业务复杂度:
- 简单 CRUD:单体就好
- 业务逻辑复杂:考虑拆分
流量规模:
- 小流量:单体就好
- 高流量:考虑拆分
拆分策略
垂直拆分
// 按业务功能拆分
/user-service
- 用户管理
- 权限管理
/order-service
- 订单处理
- 订单查询
/payment-service
- 支付处理
- 退款管理
水平拆分
// 按流量拆分
/user-service-master
/user-service-slave-1
/user-service-slave-2
分布式挑战
数据一致性
# 分布式事务问题
def process_order(user_id, amount):
# 1. 扣减库存
inventory.reduce_stock(amount)
# 2. 创建订单
order = order_service.create_order(user_id, amount)
# 3. 支付
payment_service.charge(user_id, amount)
# 问题:如果支付失败,库存已经扣了
解决:使用 Saga 模式或 TCC。
# Saga 模式
class CreateOrderSaga:
def execute(self, user_id, amount):
try:
# 步骤 1:扣减库存
inventory_service.reduce_stock(amount)
# 步骤 2:创建订单
order = order_service.create_order(user_id, amount)
# 步骤 3:支付
payment_service.charge(user_id, amount)
return order
except Exception as e:
# 补偿操作
inventory_service.restore_stock(amount)
raise e
服务发现
// 服务发现问题
const users = [
'http://user-service-1:3000',
'http://user-service-2:3000',
'http://user-service-3:3000'
];
// 简单的负载均衡
function getUserService() {
return users[Math.floor(Math.random() * users.length)];
}
// 问题:服务挂了怎么办?
解决:使用服务注册中心。
// 使用 Consul 或 Eureka
const consul = require('consul')();
// 服务注册
async function registerService(serviceName, serviceUrl) {
await consul.agent.service.register({
name: serviceName,
address: serviceUrl,
check: {
http: `http://${serviceUrl}/health`,
interval: '10s'
}
});
}
// 服务发现
async function discoverService(serviceName) {
const services = await consul.agent.service.list();
return services[serviceName];
}
消息队列
# 服务解耦
from celery import Celery
app = Celery('orders', broker='redis://localhost:6379/0')
@app.task
def process_order(order_id):
order = get_order(order_id)
# 异步处理
send_notification(order.user_id, '订单已创建')
update_statistics(order)
send_analytics(order)
踩过的坑
坑一:过度拆分
一开始把所有东西都拆成独立服务,结果管理复杂。
解决:合并相关服务,减少服务数量。
坑二:数据拆分不清晰
数据拆分不清晰,导致数据访问复杂。
解决:按照业务域拆分数据,明确所有权。
坑三:监控困难
服务多了,监控变得困难。
解决:使用分布式追踪和集中监控。
# 使用 Jaeger 追踪请求
from opentelemetry import trace
from opentelemetry.exporter.jaeger import JaegerExporter
tracer = trace.get_tracer(__name__)
@tracer.start_as_current_span("process_order")
def process_order(order_id):
with tracer.start_as_span("get_order"):
order = get_order(order_id)
with tracer.start_as_span("process_payment"):
process_payment(order)
写在最后
系统设计这东西,不只是技术,是业务和团队的映射。
解决了:
- 扩展性问题
- 团队协作问题
- 部署效率问题
带来了:
- 复杂度增加
- 运维成本
- 数据一致性挑战
拆分之前先评估:
- 业务规模
- 团队规模
- 技术能力
- 运维成本
不是所有系统都需要拆分,有时候单体架构更简单。
这次系统设计改造花了两个月,从单体到分布式。改造完成后,系统吞吐量提升了 5 倍,可用性从 99.5% 提升到 99.9%。
可用性说明:本文发布于 2021 年 1 月,距今已超过五年。文中涉及的软件版本、接口、下载地址、命令参数和操作界面可能已经发生变化,部分方案在当前环境下可能失效。请结合官方最新文档核对后再操作,生产环境使用前务必先行验证。
版权声明: 本文首发于 指尖魔法屋-系统设计:单体不够用了之后(https://blog.thinkmoon.cn/post/102-system-design-monolith-distributed-architecture/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。