从单体到服务网格:华为云 CSE + Spring Cloud Huawei 微服务治理实战
摘要: 本文复盘订单中心从单体拆分为 5 个微服务后,用华为云 CSE(微服务引擎)+ Spring Cloud Huawei 解决注册发现、配置中心、熔断降级、分布式事务、链路追踪五大治理问题的全过程。覆盖 ServiceComb 注册发现、动态配置热更新、HystrixResilience4J 熔断策略、SEATA AT 模式分布式事务、APM 全链路追踪 6 个核心实现。包含 7 段可运行代码、4 张架构/流程图、5 个真实踩坑案例和拆分前后对比数据(单服务 2.8 万行代码拆为 5 服务平均 5600 行,单服务故障不再拖垮全局,发布频率从 2 周 1 次提至每日可发)。
一、为什么订单中心要拆微服务?
1.1 单体架构的瓶颈
订单中心最早是一个 2.8 万行代码的单体 SpringBoot 应用,包含订单、库存、优惠券、支付、通知 5 个业务模块。第一年还能跑,第二年问题集中爆发:

最痛的一次:通知模块里有个未捕获的 OOM,导致整个 order-service 进程挂掉,下单全停 4 分钟。明明只是发短信的代码出问题,却让订单业务跟着陪葬。
1.2 微服务拆分原则
按业务能力拆,不按团队边界拆(团队会变,业务能力稳定):

拆分后每个服务独立部署、独立扩容、独立技术栈。通知模块以后想用 Go 重写,对其他服务零影响。
1.3 拆分后的治理挑战
拆完服务,5 个新问题立刻冒出来:
| 问题 | 单体时怎么解决 | 微服务后怎么解决 |
|---|---|---|
| 服务怎么找到彼此 | 同 JVM 方法调用 | 注册发现(CSE ServiceComb) |
| 配置怎么统一管理 | 一个 application.yml | 配置中心(CSE Config Center) |
| 下游挂了怎么不让上游跟着挂 | try-catch | 熔断降级(Resilience4J) |
| 跨服务事务怎么一致 | 同 DB 事务 | 分布式事务(SEATA) |
| 一次请求跨多个服务怎么排查 | 单机日志 | 链路追踪(APM) |
这就是华为云 CSE 要解决的事。
二、华为云 CSE 架构与 Spring Cloud Huawei
2.1 CSE 是什么
华为云 CSE(Cloud Service Engine,微服务引擎)提供注册发现、配置管理、服务治理、链路追踪的托管能力。它支持两种接入方式:
- Java Chassis / Spring Cloud Huawei:华为自研 SDK,与 CSE 深度集成,信创原生
- 兼容 Spring Cloud Netflix:用 Eureka/Nacos 替代 ServiceComb,平滑迁移
我选 Spring Cloud Huawei,理由:
| 考量 | Spring Cloud Netflix | Spring Cloud Huawei |
|---|---|---|
| 注册中心 | 自建 Eureka/Nacos | CSE 托管,免运维 |
| 配置中心 | 自建 Nacos/Config Server | CSE 托管,加密配置 |
| 信创适配 | 需自己验证 | 华为官方支持鲲鹏 + openEuler |
| 治理能力 | Hystrix(已停更) | Resilience4J + 华为治理策略 |
| 与 APM 集成 | 需自己接 | 原生打通 APM 链路 |
2.2 整体架构

三、注册发现:ServiceComb 接入
3.1 依赖引入
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-huawei-bom</artifactId>
<version>1.11.0-1.6.x</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-starter-huawei-servicecomb</artifactId>
</dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>spring-cloud-starter-huawei-governance</artifactId>
</dependency>
</dependencies>
3.2 配置接入 CSE
spring:
application:
name: order-service
cloud:
servicecomb:
discovery:
enabled: true
address: https://cse.cn-north-4.myhuaweicloud.com
appName: order-center
serviceName: ${spring.application.name}
version: 1.0.0
dataCenter: cn-north-4
config:
enabled: true
serverAddr: https://cse.cn-north-4.myhuaweicloud.com
watch:
enabled: true
credentials:
enabled: true
accessKey: ${CSE_AK}
secretKey: ${CSE_SK}
cipher: default
启动后 order-service 自动注册到 CSE Service Center,可在控制台看到服务实例:

关键认知:服务间调用不再写死 IP,而是从 Service Center 拿实例列表 + 客户端负载均衡。stock-service 扩容到 3 个实例,order-service 自动感知并轮询调用。
3.3 Feign 调用其他服务
@FeignClient(name = "stock-service", path = "/api/stock")
public interface StockFeignClient {
@PostMapping("/deduct")
Result<Void> deduct(@RequestBody DeductRequest req);
@GetMapping("/{sku}")
Result<StockVO> query(@PathVariable String sku);
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final StockFeignClient stockFeignClient;
public Long create(OrderCreateDTO dto) {
Result<Void> r = stockFeignClient.deduct(new DeductRequest(dto.getItems()));
if (!r.isSuccess()) {
throw new BusinessException("库存扣减失败: " + r.getMessage());
}
return orderRepository.insert(buildOrder(dto));
}
}
踩坑①:Feign 调用传 traceId 丢失
早期 Feign 拦截器没配,跨服务调用后 APM 链路断成两截。Spring Cloud Huawei 默认透传x-b3-traceid,但前提是上游也用 Huawei SDK。混合调用(如调用第三方 HTTP)需手动在 RequestInterceptor 里塞 header。
四、配置中心:动态配置热更新
4.1 为什么不用本地 application.yml
单体时一个 application.yml 够了,微服务后 5 个服务 × 3 个环境 = 15 份配置,改一个限流阈值要改 15 个文件 + 重启 5 个服务。配置中心解决"一处修改,处处生效,无需重启"。
4.2 在 CSE 控制台创建配置
CSE Config Center 按 应用名/服务名/版本/环境 维度管理配置。创建 order-center/order-service/1.0.0/production 的配置:
# CSE 配置中心的内容
order:
rate-limit:
qps: 500
circuit-breaker:
failure-rate-threshold: 50
slow-call-rate-threshold: 30
wait-duration-in-open-state: 10s
retry:
max-attempts: 3
wait-duration: 200ms
4.3 应用侧动态刷新
@RestController
@RefreshScope
@RequiredArgsConstructor
public class OrderController {
@Value("${order.rate-limit.qps}")
private int rateLimitQps;
@Value("${order.circuit-breaker.failure-rate-threshold}")
private float failureRateThreshold;
@PostMapping("/api/orders")
public Result<Long> create(@RequestBody OrderCreateDTO dto) {
log.info("当前限流配置: qps={}, failureThreshold={}", rateLimitQps, failureRateThreshold);
return Result.success(orderService.create(dto));
}
}
在 CSE 控制台把 qps 从 500 改成 1000,@RefreshScope 的 Bean 会自动重建,无需重启即生效。这个能力在大促时调限流阈值特别有用——以前改配置要凌晨重启 5 个服务,现在控制台点一下 30 秒全生效。
踩坑②:@RefreshScope 与 @Transactional 共用导致 Bean 代理失效
早期 OrderService 同时标了@RefreshScope和@Transactional,结果事务不生效。根因是 RefreshScope 创建的代理覆盖了事务代理。解法:把配置项单独抽到一个@RefreshScope的OrderConfigPropertiesBean,业务 Bean 注入它而非直接@Value。
五、熔断降级:Resilience4J 实战
5.1 雪崩的教训
没接熔断前,stock-service 慢查询导致 order-service 调用超时,order-service 线程池被打满,进而 coupon-service 调 order 也超时,全链路雪崩:

5.2 熔断 + 限流 + 重试配置
Spring Cloud Huawei 内置 Resilience4J 治理能力,在 CSE 控制台配置治理策略:
servicecomb:
governance:
# 全局限流
RateLimiting:
order-service:
rules:
- match:
apiPath:
prefix: /api/orders
rate: 500
# 熔断
CircuitBreaker:
order-service:
rules:
- match:
apiPath:
prefix: /api/stock
failureRateThreshold: 50
slowCallRateThreshold: 30
slowCallDurationThreshold: 2s
waitDurationInOpenState: 10s
minimumNumberOfCalls: 20
slidingWindowSize: 100
# 重试
Retry:
order-service:
rules:
- match:
apiPath:
prefix: /api/stock
maxAttempts: 3
waitDuration: 200ms
retryOnStatus: [502, 503]
5.3 降级 fallback
@FeignClient(name = "stock-service", path = "/api/stock",
fallback = StockFeignClientFallback.class)
public interface StockFeignClient {
@PostMapping("/deduct")
Result<Void> deduct(@RequestBody DeductRequest req);
}
@Component
@Slf4j
public class StockFeignClientFallback implements StockFeignClient {
@Override
public Result<Void> deduct(DeductRequest req) {
log.warn("stock-service 熔断降级,订单暂存待后续扣减: {}", req.getOrderId());
StockFallbackEvent event = new StockFallbackEvent(req.getOrderId(), req.getItems());
applicationContext.publishEvent(event);
return Result.success(null);
}
}
降级策略:stock-service 熔断时,订单仍可创建(库存预扣已在 Redis 完成),把"最终扣减"事件写入 Kafka 待 stock-service 恢复后补扣。核心思路:非核心链路挂了,降级而非报错。
5.4 熔断效果实测
模拟 stock-service 故障(kill 进程),观察 order-service 行为:
| 阶段 | 调用结果 | 耗时 | 说明 |
|---|---|---|---|
| 0-20 次调用 | 超时失败 | 3s | 熔断器 CLOSED,正常调用 |
| 21 次起 | 直接降级 | 2ms | 熔断器 OPEN,快速失败 |
| 10s 后 | 半开探测 | 50ms | 熔断器 HALF_OPEN,放一个请求试探 |
| stock 恢复 | 恢复正常 | 80ms | 熔断器 CLOSED |
关键收益:熔断前每次失败要等 3s 超时,熔断后 2ms 快速失败,下游恢复后自动探测恢复,全程无需人工干预。
踩坑③:熔断窗口设太小导致误判
早期slidingWindowSize=10,偶发 2 次失败就触发熔断。改成 100 +minimumNumberOfCalls=20后,至少 20 次调用且失败率超 50% 才熔断,误判率降到 0。
六、分布式事务:SEATA AT 模式
6.1 跨服务事务的难题
下单要扣库存(stock-service)+ 用优惠券(coupon-service)+ 创订单(order-service)。任一失败都要回滚。单体时一个 @Transactional 搞定,微服务后怎么办?

6.2 SEATA AT 模式接入
华为云 CSE 集成了 SEATA,AT 模式对业务零侵入(只需加 @GlobalTransactional)。
SEATA Server 由 CSE 托管,应用侧只需引入 SDK:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
seata:
enabled: true
tx-service-group: order-center-tx-group
service:
vgroup-mapping:
order-center-tx-group: default
grouplist:
default: seata-server.cse.svc.cluster.local:8091
registry:
type: servicecomb
config:
type: servicecomb
6.3 业务代码
@Service
@RequiredArgsConstructor
public class OrderTransactionService {
private final OrderRepository orderRepository;
private final StockFeignClient stockFeignClient;
private final CouponFeignClient couponFeignClient;
@GlobalTransactional(timeoutMills = 60000, name = "create-order-tx")
public Long createOrder(OrderCreateDTO dto) {
Order order = buildOrder(dto);
orderRepository.insert(order);
stockFeignClient.deduct(new DeductRequest(order.getId(), dto.getItems()));
if (dto.getCouponId() != null) {
couponFeignClient.use(new UseCouponRequest(order.getId(), dto.getCouponId()));
}
return order.getId();
}
}
AT 模式原理:

踩坑④:SEATA AT 模式不支持嵌套子查询
有一段库存扣减 SQL 写了UPDATE stock SET qty = qty - ? WHERE id IN (SELECT id FROM stock WHERE ...),SEATA 生成 undo log 时解析失败。改成先查再更 + 行锁后解决。
踩坑⑤:全局事务超时设置过短
早期timeoutMills=10000,结果优惠券服务一次慢查询导致全局事务超时回滚,但本地事务已提交,出现部分提交。调到 60000ms + 各分支事务超时 < 全局超时后稳。
七、链路追踪:APM 跨服务排障
7.1 一次跨服务排障实录
某次用户反馈"下单偶尔慢",单体时看一个日志文件就行,微服务后请求跨 5 个服务,没链路追踪根本没法查。
Spring Cloud Huawei 默认透传 x-b3-traceid,APM 自动采集:

在 APM 控制台输入 traceid,直接看到完整调用链:order(80ms) → coupon(280ms) → DB(250ms),一眼定位是 coupon DB 慢查询。点开 span 详情还能看 SQL 语句和执行计划。
7.2 自定义业务埋点
@Around("@annotation(businessSpan)")
public Object traceBusiness(ProceedingJoinPoint pjp, BusinessSpan businessSpan) throws Throwable {
Span span = tracer.spanBuilder(businessSpan.name()).startSpan();
try (Scope scope = span.makeCurrent()) {
span.setAttribute("order.userId", currentUserId());
span.setAttribute("order.amount", currentAmount());
return pjp.proceed();
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end();
}
}
在 APM 看板可以按 order.amount > 10000 筛选慢请求,定位大额订单的特殊问题。
八、拆分前后对比
| 维度 | 单体 | 微服务 + CSE | 变化 |
|---|---|---|---|
| 单服务代码行数 | 28,000 | 平均 5,600 | -80% |
| 编译时间 | 8min | 平均 1.5min | -81% |
| 发布频率 | 2 周 1 次 | 每日可发 | 14x |
| 单模块故障影响面 | 全站挂 | 仅该服务 | 隔离 |
| 跨服务排障耗时 | 1h+ 翻日志 | 5min 看 APM | -92% |
| 配置变更生效 | 重启 5 服务 | 30s 热更新 | -99% |
| 大促扩容粒度 | 整个单体 | 按服务独立扩 | 精细 |
| 团队并行度 | 互相阻塞 | 5 团队并行 | 5x |
九、总结与展望
9.1 实践总结
微服务拆分 + CSE 治理后,三个最大收益:
- 故障隔离:通知模块 OOM 不再拖垮下单主链路
- 独立演进:5 个团队各自发版,互不阻塞
- 精细扩容:大促时只扩 order + stock,不浪费资源扩通知
三个最值得的决策:
- 按业务能力拆而非按团队拆,边界稳定
- 用 SEATA AT 模式而非 TCC,业务零侵入
- 治理策略放 CSE 配置中心而非代码里,动态调整不重启
9.2 后续规划
- 试点 华为云 ASM 服务网格,把熔断/限流/重试从 SDK 下沉到 Sidecar,业务代码零治理
- 探索 ServiceComb 契约测试,防服务接口变更破坏调用方
- 接入 CSE 灰度发布,按版本路由流量实现服务级金丝雀
互动引导:你的微服务治理用的是 SDK 模式(Spring Cloud Huawei/Dubbo)还是 Sidecar 模式(Istio/ASM)?各踩过什么坑?欢迎评论区交流。
下一篇拆解 CCE 灰度发布与流量染色,解决"新版本不敢全量发"的痛点,敬请关注。
- 点赞
- 收藏
- 关注作者
评论(0)