引言
微服务不是银弹。Martin Fowler 提出的"Microservice Premium"警示我们:微服务引入的分布式事务、服务发现、运维复杂度远超单体。更常见的是"Microservice Theater"——团队表面上拆分了几十个服务,实际上共用一个数据库、部署紧耦合、任何改动都需要跨团队协调。
真正的微服务应该围绕业务边界拆分,每个服务拥有独立数据存储、独立部署、独立团队所有权。本文将深入 API 网关、服务发现、断路器、Saga、CQRS、Service Mesh 等核心模式,帮助你构建真正解耦、可靠的微服务系统。
目录
- 1. API Gateway 模式
- 2. 服务注册与发现
- 3. 断路器模式
- 4. Saga 分布式事务
- 5. CQRS 与事件溯源
- 6. Sidecar 与 Service Mesh
- 7. 微服务拆分策略
- 8. 总结与最佳实践
- 延伸阅读
1. API Gateway 模式
API 网关是客户端与服务群之间的统一入口,承担路由、聚合、认证、限流、日志和协议转换职责,避免客户端直接调用多个微服务。
1.1 Nginx 路由示例
upstream user_service {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
server {
listen 443 ssl;
server_name api.example.com;
location /api/v1/users/ {
proxy_pass http://user_service;
proxy_set_header X-Request-Id $request_id;
}
location /api/v1/orders/ {
proxy_pass http://order_service;
}
}
1.2 Kong 限流与 JWT 插件
_format_version: "3.0"
services:
- name: user-service
url: http://user-service:8080
routes:
- name: user-route
paths: [/api/v1/users]
plugins:
- name: rate-limiting
config: { minute: 100, policy: redis, redis_host: redis.local }
- name: jwt
config: { key_claim_name: iss }
最佳实践:将认证、限流、日志等横切关注点集中在网关,避免在每个微服务中重复实现。
2. 服务注册与发现
微服务实例的数量和地址动态变化,服务发现让服务能自动找到彼此,而无需硬编码地址。
2.1 Consul 配置
datacenter = "dc1"
server = true
bootstrap_expect = 3
service {
name = "order-service"
port = 8080
tags = ["v1", "production"]
check {
http = "http://localhost:8080/health"
interval = "10s"
timeout = "3s"
}
}
2.2 Kubernetes 内置服务发现
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
其他服务可通过 order-service.<namespace>.svc.cluster.local 访问。
选型建议:etcd 适合配置存储,Consul 提供完整服务网格,Kubernetes Service 与容器编排深度集成。
3. 断路器模式
3.1 状态机原理
- Closed:请求正常通过,统计失败次数
- Open:失败率超阈值,所有请求快速失败
- Half-Open:超时后允许少量探测请求,成功则恢复
success
┌─────────────┐
│ ▼
CLOSED ──failure──▶ OPEN ──timeout──▶ HALF-OPEN
▲ │
└──────────── success ──────────────┘
3.2 Go 实现(sony/gobreaker)
var cb = gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "order-service",
MaxRequests: 3,
Interval: 10 * time.Second,
Timeout: 60 * time.Second,
ReadyToTrip: func(c gobreaker.Counts) bool {
ratio := float64(c.TotalFailures) / float64(c.Requests)
return c.Requests >= 5 && ratio >= 0.6
},
})
func CallOrderService(id string) (*Order, error) {
result, err := cb.Execute(func() (interface{}, error) {
resp, err := http.Get("http://order-service/api/v1/orders/" + id)
if err != nil { return nil, err }
defer resp.Body.Close()
if resp.StatusCode >= 500 {
return nil, errors.New("server error")
}
return parseOrder(resp)
})
if err != nil {
if errors.Is(err, gobreaker.ErrOpenState) {
return getCachedOrder(id) // 降级到缓存
}
return nil, err
}
return result.(*Order), nil
}
4. Saga 分布式事务
跨服务事务无法用传统 ACID,Saga 通过一系列本地事务 + 补偿操作实现分布式业务逻辑。
4.1 编排式(Orchestration)
由中心协调器调度各服务:
[Order Service] ──create──▶ [Payment Service]
│ │
▼ ▼
[Inventory] ──reserve──▶ [Shipping Service]
│
▼ (失败补偿)
[Payment] ◀──refund── [Order Service]
4.2 协调式(Choreography)
每个服务通过事件驱动,无中心协调:
// Order Service 发出事件
func (s *OrderService) CreateOrder(req OrderRequest) error {
order := s.repo.Create(req)
s.eventBus.Publish(OrderCreated{
OrderID: order.ID, UserID: req.UserID, Amount: req.Amount,
})
return nil
}
// Payment Service 监听并响应
func (s *PaymentService) HandleOrderCreated(e OrderCreated) {
if err := s.charge(e.UserID, e.Amount); err != nil {
s.eventBus.Publish(PaymentFailed{OrderID: e.OrderID})
return
}
s.eventBus.Publish(PaymentSuccess{OrderID: e.OrderID})
}
选择建议:2-3 个服务的简单流程用协调式;5 个以上服务的复杂流程用编排式,便于追踪和回滚。
5. CQRS 与事件溯源
CQRS(Command Query Responsibility Segregation)将读写操作分离到不同模型甚至不同存储。
事件溯源(Event Sourcing)不存储当前状态,而是存储所有变更事件,通过重放还原状态:
传统 CRUD:UPDATE users SET balance = 100 WHERE id = 1
事件溯源:
Event 1: UserCreated(id=1, balance=0)
Event 2: DepositMade(id=1, amount=100)
Event 3: WithdrawalMade(id=1, amount=50)
Event 4: DepositMade(id=1, amount=50)
当前余额 = 重放 = 100
适用:金融审计、高并发读写分离、协作型应用。不适用:简单 CRUD、团队不熟悉事件驱动。
6. Sidecar 与 Service Mesh
Sidecar 模式将服务发现、负载均衡、加密、监控等通用功能剥离为独立进程。Service Mesh 是 Sidecar 的规模化应用,代表项目有 Istio 和 Linkerd。
Istio 金丝雀发布配置:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts: [order-service]
http:
- match:
- headers: { x-canary: { exact: "true" } }
route:
- destination: { host: order-service, subset: canary, weight: 100 }
- route:
- destination: { host: order-service, subset: stable, weight: 90 }
- destination: { host: order-service, subset: canary, weight: 10 }
警告:Service Mesh 引入显著的运维复杂性和网络延迟(每请求增加 2-5ms),团队小于 20 人或服务少于 10 个时谨慎引入。
7. 微服务拆分策略
最有效的方法基于 DDD 的限界上下文(Bounded Context):
- 事件风暴:召集领域专家与工程师,识别业务流程中的事件、命令、聚合
- 识别限界上下文:将聚合按语义边界分组
- 验证独立性:上下文之间可通过 API 或事件通信,不共享数据库
- 渐进式迁移:使用 Strangler Fig 模式从单体中逐步剥离
反模式:
- 按技术层拆分(“用户服务”、“支付服务”)而非业务边界
- 拆分过细,一个请求跨越 10+ 服务
- 多个服务共享同一数据库,失去独立部署能力
8. 总结与最佳实践
| 模式 | 适用场景 | 常见陷阱 |
|---|---|---|
| API Gateway | 所有对外微服务架构 | 把业务逻辑放在网关 |
| 服务发现 | 动态扩缩容环境 | 健康检查配置不当 |
| 断路器 | 服务间远程调用 | 没有降级策略 |
| Saga | 跨服务业务流程 | 补偿逻辑未充分测试 |
| CQRS | 读写比例悬殊系统 | 简单 CRUD 过度使用 |
| Service Mesh | 大规模微服务集群 | 小团队过早引入 |
最佳实践:
- 每个服务拥有独立数据库(Database per Service)
- 使用 Kafka、RabbitMQ 异步解耦
- 每个服务可独立部署、独立测试
- 远程调用必配超时、重试、断路器
- 实施分布式追踪(OpenTelemetry)
- Monolith First:从单体开始,按需拆分
延伸阅读
- Sam Newman, Building Microservices 2nd Edition, O’Reilly
- Chris Richardson, Microservices Patterns, Manning
- microservices.io Pattern Catalog
- Martin Fowler, Monolith First
- Istio 官方文档
- Eric Evans, Domain-Driven Design, Addison-Wesley
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。