云原生微服务踩坑记:187个服务降到23个,故障率降低90%
作者按: 去年双11前,我们的微服务系统有187个服务,故障率居高不下。经过半年重构,降到23个核心服务,故障率降低90%。本文是真实复盘,包含完整代码和血泪教训。阅读约25分钟。
引言:我们怎么把微服务玩坏的?
去年双11前,我们做了一个"艰难的决定":把运行了5年的单体应用拆成微服务。
初衷很美好:
- 团队独立开发部署,提升效率
- 故障隔离,一个服务挂了不影响全局
- 技术栈灵活,不同服务可以用不同语言
现实很骨感:
- 分布式链路追踪缺失,排查问题靠"猜"
- 服务雪崩,一个慢查询拖垮整个系统
- 配置管理混乱,改个开关要发版
- 版本依赖复杂,升级一个服务导致连锁反应
最惨的时候,我们有187个微服务,运维成本巨大,故障率反而比单体应用高。
经过半年的踩坑和实践,我们重构为23个核心服务,故障率降低90%。本文将毫无保留地分享这个过程。
技术栈: Spring Cloud Alibaba + Nacos + Sentinel + SkyWalking + Kubernetes
一、微服务架构设计原则
1.1 服务拆分策略
错误的拆分方式(我们踩过的坑):
❌ 按技术层拆分:UserController、UserService、UserDAO各成一个服务
- 问题:每次需求变更要改3个服务,发布顺序还要考虑依赖
❌ 拆得太细:一个只有2个接口的服务也单独部署
- 问题:服务数量爆炸(我们最多时有187个微服务),运维成本巨大
❌ 数据库共享:所有服务连同一个数据库
- 问题:一个服务的慢SQL会影响其他服务,完全没有隔离性
正确的拆分原则(DDD领域驱动设计):
✅ 按业务边界拆分:订单服务、商品服务、用户服务、支付服务...
- 好处:每个服务职责清晰,团队自治
✅ 数据库独享:每个服务有自己的数据库
- 好处:故障隔离,可以针对业务特点选择数据库(订单用MySQL,搜索用ElasticSearch)
✅ 前后端分离 + BFF层:
┌─────────────────────────────────────┐
│ 前端(Web/App) │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ BFF层(Backend For Frontend) │
│ - 聚合多个微服务的数据 │
│ - 适配前端的数据格式 │
│ - 处理鉴权、限流等横切逻辑 │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ 微服务层 │
│ 订单服务 | 商品服务 | 用户服务 ... │
└─────────────────────────────────────┘
实战案例:电商平台的微服务拆分
| 服务名 | 职责 | 数据库 | 接口数量 | QPS |
|---|---|---|---|---|
| 用户服务 | 注册、登录、用户信息管理 | MySQL | 15 | 5000 |
| 商品服务 | 商品CRUD、库存管理 | MySQL + Redis | 23 | 8000 |
| 订单服务 | 下单、订单查询、订单取消 | MySQL + Redis | 18 | 3000 |
| 支付服务 | 支付、退款、对账 | MySQL | 12 | 1000 |
| 搜索服务 | 商品搜索、推荐 | Elasticsearch | 8 | 10000 |
| 通知服务 | 短信、邮件、App推送 | MongoDB | 6 | 2000 |
拆分后的好处:
- 订单服务压力大?单独扩容订单服务的Pod(K8s)
- 搜索服务要升级ES版本?不影响其他服务
- 新同事入职?只需了解他负责的服务,不用掌握整个系统
1.2 服务通信方式选择
微服务之间如何通信?我们有3种方式:
| 通信方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| REST API | 同步调用,需要立即返回结果 | 简单直观,调试方便 | 耦合性强,性能一般 |
| RPC(Dubbo/gRPC) | 高性能同步调用 | 性能高(二进制传输),有服务发现 | 学习成本,调试不如HTTP直观 |
| 消息队列(Kafka/RocketMQ) | 异步调用,不需要立即返回 | 解耦,削峰填谷,失败可重试 | 复杂性高,需要保证消息不丢失 |
我们的实践:
┌─────────────┐
│ 下单请求 │
└──────┬──────┘
↓
┌──────────────────────────────────────┐
│ 订单服务(主流程) │
│ 1. 创建订单(REST API,同步) │
│ → 调用商品服务扣减库存 │
│ → 调用支付服务预扣款 │
│ 2. 发送消息(异步) │
│ → 通知物流服务发货 │
│ → 通知通知服务发送确认短信 │
│ → 通知大数据平台记录订单数据 │
└──────────────────────────────────────┘
关键原则:
- 核心流程用同步(下单必须立即知道成功还是失败)
- 边缘业务用异步(物流服务慢一点没关系,消息队列削峰)
- 禁止使用循环调用(A→B→C→A,会形成死循环)
二、服务治理核心组件
2.1 服务注册与发现(Nacos)
问题: 微服务实例是动态变化的(扩容、缩容、实例崩溃),调用方怎么知道有哪些实例可用?
解决方案: 服务注册与发现
java
// 1. 服务启动时报到Nacos
@SpringBootApplication
@EnableDiscoveryClient // 启用服务发现
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// 2. 配置文件(application.yml)
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848 # Nacos服务器地址
namespace: dev # 命名空间(隔离开发/测试/生产)
group: ECOMMERCE # 分组(同一分组下的服务才能互相发现)
config:
server-addr: 192.168.1.100:8848
file-extension: yaml # 配置格式
// 3. 调用其他服务(通过服务名,不用写死IP)
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{userId}")
public Order getOrder(@PathVariable Long userId) {
// 不用写 http://192.168.1.101:8080/user/...
// 直接写服务名,Nacos会自动做负载均衡
User user = restTemplate.getForObject(
"http://user-service/user/" + userId,
User.class
);
// ...
}
}
Nacos的高级特性:
- 健康检查:Nacos定期ping服务实例,不健康的实例会被摘除
- 自我保护机制:网络分区时,不会盲目摘除实例(避免"雪崩")
- 配置中心:不用重启服务就能改配置(如开关某个功能)
yaml
# Nacos配置中心示例
# 在Nacos控制台添加配置:order-service.yaml
# 功能开关
feature:
coupon: true # 优惠券功能
flash_sale: false # 秒杀功能(默认关闭)
# 限流配置
ratelimit:
qps: 1000 # 每秒最多1000次请求
# 在代码中动态获取配置
@RefreshScope // 配置变更时自动刷新
@RestController
public class OrderController {
@Value("${feature.coupon}")
private boolean couponEnabled;
@PostMapping("/order")
public Response createOrder(@RequestBody OrderRequest req) {
if (couponEnabled) {
// 使用优惠券逻辑
}
// ...
}
}
2.2 服务容错(Sentinel)
问题: 一个服务响应慢,会导致调用方线程池耗尽,进而引发雪崩。
场景还原:
用户请求 → 订单服务 → 支付服务(响应慢,5秒才返回)
→ 线程池耗尽(默认200个线程)
→ 新请求进不来
→ 订单服务也挂了
→ 所有依赖订单服务的服务都挂了
→ 整个系统崩溃
解决方案: 限流、熔断、降级(Sentinel)
java
// 1. 引入Sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
// 2. 配置Sentinel控制台地址
spring:
cloud:
sentinel:
transport:
dashboard: 192.168.1.101:8080 # Sentinel控制台
port: 8719 # 与控制台通信的端口
// 3. 定义资源(需要保护的方法)
@Service
public class OrderService {
// 方式1:注解方式(推荐)
@SentinelResource(value = "createOrder",
blockHandler = "createOrderBlockHandler", // 被限流/熔断时的处理方法
fallback = "createOrderFallback") // 抛出异常时的处理方法
public Order createOrder(OrderRequest req) {
// 正常业务逻辑
// 调用支付服务
Payment payment = paymentService.pay(req);
// ...
return order;
}
// 被限流/熔断时走这里
public Order createOrderBlockHandler(OrderRequest req, BlockException ex) {
// 返回降级结果(如:提示用户"系统繁忙,请稍后重试")
return Order.builder()
.status("FAILED")
.message("系统繁忙,请稍后重试")
.build();
}
// 抛出异常时走这里
public Order createOrderFallback(OrderRequest req, Throwable t) {
// 记录日志,返回默认结果
log.error("创建订单失败", t);
return Order.builder()
.status("ERROR")
.message("创建订单失败,请联系客服")
.build();
}
}
// 4. 在Sentinel控制台配置规则(也可以代码配置)
/**
* 限流规则:
* - QPS > 1000 时,新的请求直接拒绝(快速失败)
*
* 熔断规则:
* - 响应时间 > 2000ms 的请求占比 > 50% 时,熔断10秒
* - 熔断期间,所有请求直接走降级方法
*/
Sentinel的三种保护机制:
| 机制 | 作用 | 配置示例 |
|---|---|---|
| 限流(Flow Control) | 控制QPS,防止系统被冲垮 | QPS > 1000时拒绝请求 |
| 熔断(Circuit Breaking) | 调用慢/异常多时,暂时切断调用 | 错误率 > 50% 时熔断10秒 |
| 降级(Fallback) | 调用失败时,返回默认结果 | 抛出异常时返回"系统维护中" |
实战经验:
- 核心接口必须配置限流(如下单、支付),防止恶意攻击或流量突增
- 调用第三方服务必须配置熔断(如支付、短信),第三方不可控
- 降级方法要友好(不要直接抛异常给用户),可以返回缓存数据或默认结果
2.3 分布式链路追踪(SkyWalking)
问题: 一个请求经过十几个服务,怎么快速定位哪个服务慢?
解决方案: 分布式链路追踪(SkyWalking)
请求流程:
用户请求 → API网关 → 订单服务 → 商品服务 → 库存服务 → 数据库
→ 支付服务 → 第三方支付平台
→ 通知服务 → 短信平台
问题:用户说"下单慢",到底是哪里慢?
SkyWalking的实现原理:
- 每个请求分配一个Trace ID(如:trace-12345)
- 每个服务调用是一个Span(如:span-1=订单服务,span-2=商品服务)
- Span之间有父子关系(span-2的parent是span-1)
- 记录每个Span的开始时间、结束时间、状态码
java
// 1. 引入SkyWalking依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.16.0</version>
</dependency>
// 2. 配置agent(启动时加JVM参数)
// java -javaagent:/path/to/skywalking-agent.jar -Dskywalking.agent.service_name=order-service -Dskywalking.collector.backend_service=192.168.1.102:11800 -jar order-service.jar
// 3. 在代码中获取Trace ID(方便日志关联)
@RestController
public class OrderController {
@GetMapping("/order/{orderId}")
public Order getOrder(@PathVariable String orderId) {
// 获取当前Trace ID
String traceId = TraceContext.traceId();
log.info("TraceId={}, 查询订单, orderId={}", traceId, orderId);
Order order = orderService.getOrder(orderId);
log.info("TraceId={}, 查询订单成功", traceId);
return order;
}
}
// 4. 自定义Span(追踪某个方法)
@Service
public class OrderService {
@Trace(operationName = "createOrder") // 自定义Span
public Order createOrder(OrderRequest req) {
// 这个方法会在SkyWalking中显示为一个Span
// 可以看到执行时间、是否成功等信息
// ...
}
}
SkyWalking UI界面功能:
- 拓扑图:展示服务之间的调用关系(哪些服务调用了哪些服务)
- Trace查询:输入Trace ID,查看完整调用链(哪个服务慢一目了然)
- 性能剖析:找出慢方法(如:OrderService.createOrder占用500ms)
- 告警:响应时间 > 2秒,自动发邮件/短信通知
实战案例:
问题:用户投诉"下单要5秒,太慢了"
排查过程:
1. 在SkyWalking中搜索 trace(根据订单号或用户ID)
2. 发现调用链:
- API网关:50ms
- 订单服务:200ms
- 支付服务:3500ms ← 慢在这里!
- 通知服务:100ms
3. 查看支付服务的日志,发现是调用第三方支付平台超时(设置了3秒超时)
4. 优化方案:
- 支付改成异步(下完单再慢慢扣款)
- 或者设置支付服务的熔断(第三方超时就降级)
三、配置管理与环境变量
3.1 配置中心(Nacos)
问题: 改一个配置(如:关闭某个功能),要重新打包、发布、重启服务?
解决方案: 配置中心(Nacos/Apollo)
java
// 传统方式(要改代码、重新打包)
@Component
public class OrderService {
private boolean couponEnabled = true; // 写死在代码里
public void createOrder() {
if (couponEnabled) { // 要关闭?改代码,重新发布
// ...
}
}
}
// 配置中心方式(动态刷新,不用重启)
// 在Nacos中配置 order-service.properties
coupon.enabled=true
flash.sale.enabled=false
ratelimit.qps=1000
// 代码中读取
@RestController
@RefreshScope // 配置变更时自动刷新
public class OrderController {
@Value("${coupon.enabled}")
private boolean couponEnabled;
@PostMapping("/order")
public Response createOrder(@RequestBody OrderRequest req) {
if (couponEnabled) {
// 使用优惠券
}
// ...
}
}
配置中心的好处:
- 动态刷新:改配置不用重启服务(秒级生效)
- 版本管理:配置也有版本,可以回滚
- 权限控制:谁改了配置?有审计日志
- 灰度发布:1%的服务器用新配置,验证OK再全量
3.2 环境变量与K8s ConfigMap
问题: 开发、测试、生产环境配置不一样,怎么管理?
解决方案: 环境变量 + Kubernetes ConfigMap/Secret
yaml
# 1. 在K8s中定义ConfigMap(配置文件)
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
namespace: production
data:
application.yaml: |
spring:
datasource:
url: jdbc:mysql://mysql-production:3306/order_db
username: ${MYSQL_USER} # 从环境变量读取
password: ${MYSQL_PASSWORD}
redis:
host: ${REDIS_HOST}
port: 6379
# 功能开关
feature:
coupon: true
flash_sale: false
---
# 2. 在K8s中定义Secret(敏感信息)
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
namespace: production
type: Opaque
data:
mysql-user: bXlzcWw= # base64编码的"mysql"
mysql-password: cGFzc3dvcmQxMjM= # base64编码的"password123"
---
# 3. 在Deployment中引用
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
template:
spec:
containers:
- name: order-service
image: order-service:latest
ports:
- containerPort: 8080
env:
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: order-service-secret
key: mysql-user
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: mysql-password
- name: REDIS_HOST
value: "redis-production"
volumeMounts:
- name: config-volume
mountPath: /config
volumes:
- name: config-volume
configMap:
name: order-service-config
最佳实践:
- 敏感信息用Secret(密码、API Key),不要写在代码里
- 环境差异用ConfigMap,不用改代码就能切换环境
- 配置加密(如:数据库密码加密存储),防止泄露
四、监控告警体系
4.1 监控指标(Prometheus + Grafana)
需要监控什么?
| 类别 | 指标 | 告警阈值 | 工具 |
|---|---|---|---|
| 业务指标 | 订单量、支付成功率、注册人数 | 订单量同比下跌 > 30% | 自定义埋点 + Prometheus |
| 性能指标 | QPS、响应时间、错误率 | P99响应时间 > 2秒 | SkyWalking + Prometheus |
| 资源指标 | CPU、内存、磁盘、网络 | CPU > 80% 持续5分钟 | Node Exporter + Prometheus |
| JVM指标 | 堆内存、GC次数、线程数 | 堆内存使用率 > 90% | JMX Exporter + Prometheus |
Prometheus配置示例:
yaml
# prometheus.yml
global:
scrape_interval: 15s # 每15秒采集一次
scrape_configs:
# 采集订单服务的指标
- job_name: 'order-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['order-service:8080']
# 采集K8s节点的指标
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# 在Spring Boot中暴露指标
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yml
management:
endpoints:
web:
exposure:
include: prometheus,health,info
metrics:
export:
prometheus:
enabled: true
Grafana仪表盘示例:
┌─────────────────────────────────────────────────┐
│ 订单服务监控仪表盘 │
├─────────────────────────────────────────────────┤
│ [面板1] QPS(每秒请求数) │
│ - 当前值:500 QPS │
│ - 图表:折线图(最近1小时) │
│ │
│ [面板2] 响应时间(P50/P99) │
│ - P50:200ms │
│ - P99:1500ms │
│ - 图表:折线图 │
│ │
│ [面板3] 错误率 │
│ - 当前值:0.5% │
│ - 告警线:> 1% 触发告警 │
│ │
│ [面板4] JVM堆内存使用率 │
│ - 当前值:65% │
│ - 告警线:> 90% 触发告警 │
└─────────────────────────────────────────────────┘
4.2 告警规则(AlertManager)
yaml
# alertmanager.yml
route:
group_by: ['alertname', 'service']
group_wait: 10s # 等待10秒,看看还有没有其他告警
group_interval: 10s # 发送告警的间隔
repeat_interval: 1h # 重复告警的间隔
receiver: 'webhook' # 接收者
receivers:
- name: 'webhook'
webhook_configs:
- url: 'http://alert-handler:8080/alert' # 自定义的告警处理服务
send_resolved: true # 问题恢复时也发送通知
# 在Prometheus中定义告警规则
# prometheus.rules.yml
groups:
- name: order-service-alerts
rules:
# 规则1:订单服务不可达
- alert: OrderServiceDown
expr: up{job="order-service"} == 0
for: 1m # 持续1分钟才告警(防止误报)
labels:
severity: critical
annotations:
summary: "订单服务不可达"
description: "订单服务 {{ $labels.instance }} 已经下线超过1分钟"
# 规则2:响应时间过长
- alert: HighResponseTime
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket{job="order-service"}[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "订单服务响应时间过长"
description: "P99响应时间为 {{ $value }} 秒,超过阈值2秒"
# 规则3:错误率过高
- alert: HighErrorRate
expr: rate(http_requests_total{job="order-service",status=~"5.."}[5m]) / rate(http_requests_total{job="order-service"}[5m]) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: "订单服务错误率过高"
description: "错误率为 {{ $value | humanizePercentage }},超过阈值1%"
告警通知方式:
- 紧急(Critical):打电话 + 发短信 + 钉钉群@所有人
- 警告(Warning):钉钉群通知 + 邮件
- 信息(Info):只记录日志,不通知
五、故障恢复与混沌工程
5.1 常见故障与恢复策略
| 故障类型 | 原因 | 恢复策略 | 自动化程度 |
|---|---|---|---|
| 服务崩溃 | OOM、代码bug、依赖服务不可用 | K8s自动重启Pod | ✅ 全自动 |
| 数据库慢查询 | 索引缺失、数据量过大 | DBA优化SQL + 限流 | ⚠️ 半自动(需要DBA介入) |
| 网络分区 | 机房故障、网络设备故障 | 流量切换到其他机房 | ✅ 全自动(多机房部署) |
| 配置错误 | 人为误操作 | 回滚配置(Nacos/Apollo支持) | ✅ 全自动 |
| 依赖服务不可用 | 第三方服务挂了 | 熔断 + 降级(返回缓存数据) | ✅ 全自动 |
K8s的自动恢复机制:
yaml
# 1. 健康检查(Liveness Probe)
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: order-service
livenessProbe: # 存活探针(检查服务是否还活着)
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60 # 启动后60秒才开始检查
periodSeconds: 10 # 每10秒检查一次
failureThreshold: 3 # 连续失败3次,重启Pod
readinessProbe: # 就绪探针(检查服务是否准备好接收流量)
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
failureThreshold: 3 # 连续失败3次,从负载均衡中摘除
# 2. 资源限制(防止OOM)
resources:
requests: # 调度时保证有这么多资源
memory: "512Mi"
cpu: "500m"
limits: # 最多使用这么多资源
memory: "1Gi"
cpu: "1000m"
# 3. 优雅关闭(SIGTERM信号)
lifecycle:
preStop: # 收到SIGTERM后,先执行这个,再杀进程
exec:
command: ["sh", "-c", "sleep 30"] # 等待30秒,让请求处理完
# 4. 自动扩缩容(HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3 # 最少3个Pod
maxReplicas: 10 # 最多10个Pod
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU使用率 > 70% 时扩容
5.2 混沌工程(ChaosBlade)
问题: 系统看起来很稳定,但真出故障时能不能快速恢复?
解决方案: 混沌工程(主动注入故障,验证系统的容错能力)
bash
# 1. 安装ChaosBlade
wget https://github.com/chaosblade-io/chaosblade/releases/download/v1.7.0/chaosblade-1.7.0-linux-amd64.tar.gz
tar -xzf chaosblade-1.7.0-linux-amd64.tar.gz
# 2. 实验1:杀掉订单服务的Pod(验证K8s能否自动重启)
./blade create k8s pod-pod restart --namespace production --labels "app=order-service" --count 1
# 观察:
# - K8s是否在1分钟内重启Pod?
# - 重启期间,请求是否有失败?
# - 重启后,服务是否正常?
# 3. 实验2:模拟网络延迟(验证超时配置是否合理)
./blade create network delay --time 3000 --interface eth0 --timeout 60
# 观察:
# - 调用这个服务的客户端是否会超时?
# - 超时后是否有降级逻辑?
# - 会不会导致线程池耗尽?
# 4. 实验3:模拟CPU满载(验证限流是否生效)
./blade create cpu fullload --timeout 60
# 观察:
# - CPU > 80% 时,限流是否生效?
# - 新请求是否被拒绝(返回429 Too Many Requests)?
# - 还是继续处理,导致服务崩溃?
# 5. 实验4:模拟数据库慢查询(验证熔断是否生效)
./blade create mysql delay --time 5000 --database order_db --timeout 60
# 观察:
# - 慢查询占比 > 50% 时,Sentinel是否会熔断?
# - 熔断期间,请求是否走降级逻辑?
混沌工程的实施步骤:
- 成立混沌工程小组(开发、测试、运维、SRE)
- 制定实验计划(哪些故障要模拟?预期结果是什么?)
- 在生产环境小规模实验(先用1%的流量,验证OK再扩大)
- 记录实验结果(发现了哪些问题?如何修复?)
- 定期演练(每季度一次,保证系统始终健壮)
六、总结与最佳实践
6.1 微服务治理 checklist
✅ 服务拆分
- 按业务边界拆分(DDD),不要按技术层拆分
- 数据库独享,不要共享数据库
- 服务数量控制在合理范围(建议 < 50个)
✅ 服务通信
- 核心流程用同步调用(REST/RPC)
- 边缘业务用异步调用(消息队列)
- 配置超时、重试、熔断(防止雪崩)
✅ 服务治理
- 服务注册与发现(Nacos/Eureka)
- 配置中心(Nacos/Apollo)
- 限流、熔断、降级(Sentinel/Resilience4j)
- 分布式链路追踪(SkyWalking/Zipkin)
✅ 监控告警
- 监控业务指标(订单量、支付成功率)
- 监控性能指标(QPS、响应时间、错误率)
- 监控资源指标(CPU、内存、磁盘)
- 配置合理的告警阈值(避免告警风暴)
✅ 故障恢复
- K8s健康检查(Liveness/Readiness Probe)
- 资源限制(防止OOM)
- 优雅关闭(SIGTERM)
- 自动扩缩容(HPA)
- 定期混沌工程演练
6.2 踩坑经验总结
坑1:服务拆分得太细
- 现象:一个简单需求要改5个服务
- 原因:没有考虑团队规模(小团队不适合太多微服务)
- 解决方案:合并低频变化的服务(如:通知服务+日志服务 → 基础设施服务)
坑2:分布式事务难题
- 现象:下单成功后,扣库存失败,数据不一致
- 原因:跨服务的事务很难保证ACID
- 解决方案:用最终一致性( Seata/消息队列+补偿机制),不要强一致性
坑3:版本依赖地狱
- 现象:升级一个基础服务,导致所有依赖它的服务都要升级
- 原因:没有做接口版本管理
- 解决方案:接口加版本号(/api/v1/order),新老版本共存,逐步迁移
坑4:测试环境不稳定
- 现象:测试环境经常挂,影响开发进度
- 原因:测试环境和生产环境差异大(配置、数据、依赖服务)
- 解决方案:用Docker Compose 或K8s Namespace搭建隔离的测试环境
参考资料
- Spring Cloud Alibaba官方文档
- Sentinel官方文档
- SkyWalking官方文档
- Kubernetes官方文档
- 《微服务架构设计模式》 - Chris Richardson 著
如果本文帮你少踩了坑,请点赞 + 收藏 + 关注三连!
讨论题:
- 你们公司的微服务是怎么拆分的?遇到过哪些坑?
- 你觉得微服务数量多少合适?我们最终定了23个核心服务
- 有没有比Nacos更好的服务注册与发现方案推荐?
期待你的分享和讨论!