现代软件架构设计模式:单体不够用了之后
现代软件架构设计模式上手并不难,难的是稳定跑起来。
下面只记真正影响结果的部分。
单体的优势
原来的项目是单体应用,部署简单、开发快速:
# app.py
from flask import Flask
from models import db, User, Order
from services import UserService, OrderService
app = Flask(__name__)
@app.route('/users/<id>')
def get_user(id):
user = UserService.get_user(id)
return jsonify(user.to_dict())
@app.route('/orders/<id>')
def get_order(id):
order = OrderService.get_order(id)
return jsonify(order.to_dict())
优势很明显:
- 部署简单:一个 jar 包,扔到服务器上就跑
- 开发快速:改代码本地调试,本地就是完整系统
- 运维简单:监控一个服务,查问题在单一进程里
什么时候该拆单体
单体应用在以下情况下开始出问题:
部署时间长:改一个功能,整个应用都要重新部署,从 5 分钟变成 30 分钟
协作困难:多个团队改同一个代码库,合并冲突频发
技术栈受限:某模块想用新语言,但单体应用不允许
性能瓶颈:整体扩展,浪费资源
迁移到微服务
服务拆分原则
不是凭感觉拆,有明确的原则:
按业务能力拆分:用户服务、订单服务、库存服务
按数据边界拆分:每个服务有自己的数据库
按团队边界拆分:一个团队负责一个服务
服务间通信
一开始同步调用,后来改用消息队列:
# 同步调用
def create_order(user_id, items):
# 调用库存服务
inventory_service.check_stock(items)
# 调用用户服务
user_service.get_user(user_id)
# 创建订单
order = Order.create(user_id, items)
return order
# 异步调用
def create_order(user_id, items):
order = Order.create(user_id, items)
# 发送事件
event_bus.publish('order_created', {
'order_id': order.id,
'items': items
})
return order
异步调用解耦了服务,但引入了新的复杂度:最终一致性。
踩坑一:分布式事务
跨服务的事务处理是个难题。
传统单体应用:
# 简单事务
with db.transaction():
order.create()
inventory.update()
user.update_points()
微服务下:
# 问题是:如果某步失败,如何回滚?
order_service.create_order()
inventory_service.check_stock()
user_service.update_points()
解决:最终一致性 + 补偿机制
def create_order(user_id, items):
try:
# 创建订单
order = OrderService.create_order(user_id, items)
# 检查库存
InventoryService.check_stock(items)
# 扣除库存
InventoryService.deduct_stock(items)
# 更新积分
UserService.update_points(user_id, order.total)
# 标记订单完成
OrderService.complete_order(order.id)
except Exception as e:
# 补偿:标记订单失败,回滚已执行的操作
OrderService.fail_order(order.id)
CompensationService.rollback(order.id)
踩坑二:数据一致性问题
用户看到订单创建成功,但库存没扣,或者积分没加。
解决:事务补偿和最终一致性检查
踩坑三:运维复杂度上升
从一个服务变成十个服务:
监控复杂:十个服务的健康状态,一个出问题就可能影响整体
日志分散:日志分散在多个服务,查问题要聚合
配置管理:每个服务的配置独立,管理复杂
依赖管理:服务间的依赖关系复杂
服务网格的引入
为解决服务间通信的问题,引入了服务网格(Istio):
# 服务网格配置
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
服务网格解决的问题:
- 服务发现:自动发现服务实例
- 负载均衡:智能流量分配
- 熔断降级:自动故障处理
- 链路追踪:跨服务的调用链路
- 安全通信:自动 mTLS 加密
什么时候用服务网格
值得用的场景:
- 服务数量多(超过 10 个)
- 服务间调用复杂
- 对可靠性要求高
- 团队有 Kubernetes 和 Istio 经验
不值得用的场景:
- 服务数量少
- 团队能力不足
- 对延迟敏感(Sidecar 增加延迟)
写在最后
架构演进这东西,不是越新越好。
单体应用有它的优势,微服务也有它的复杂性。什么时候拆、怎么拆、拆到什么程度,都需要权衡。
核心是匹配业务需求,不是追新。
这次迁移花了半年,中间有过反复。但回头看,对于业务复杂度高的项目,微服务的优势确实明显。但对于小项目,单体还是更简单。
版权声明: 本文首发于 指尖魔法屋-现代软件架构设计模式:单体不够用了之后(https://blog.thinkmoon.cn/post/22-architecture-patterns-microservices-service-mesh/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。