从单体到服务网格:华为云 CSE + Spring Cloud Huawei 微服务治理实战

举报
行者·全栈架构师 发表于 2026/09/01 22:22:21 2026/09/01
【摘要】 本文复盘订单中心从单体拆分为 5 个微服务后,用华为云 CSE(微服务引擎)+ Spring Cloud Huawei 解决注册发现、配置中心、熔断降级、分布式事务、链路追踪五大治理问题的全过程。覆盖 ServiceComb 注册发现、动态配置热更新、HystrixResilience4J 熔断策略、SEATA AT 模式分布式事务、APM 全链路追踪 6 个核心实现。

摘要: 本文复盘订单中心从单体拆分为 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 个业务模块。第一年还能跑,第二年问题集中爆发:
011-cse-microservice-governance-practice_diagram_1.png

最痛的一次:通知模块里有个未捕获的 OOM,导致整个 order-service 进程挂掉,下单全停 4 分钟。明明只是发短信的代码出问题,却让订单业务跟着陪葬。

1.2 微服务拆分原则

业务能力拆,不按团队边界拆(团队会变,业务能力稳定):
011-cse-microservice-governance-practice_diagram_2.png

拆分后每个服务独立部署、独立扩容、独立技术栈。通知模块以后想用 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 整体架构

011-cse-microservice-governance-practice_diagram_3.png

三、注册发现: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,可在控制台看到服务实例:
011-cse-microservice-governance-practice_diagram_4.png

关键认知:服务间调用不再写死 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 创建的代理覆盖了事务代理。解法:把配置项单独抽到一个 @RefreshScopeOrderConfigProperties Bean,业务 Bean 注入它而非直接 @Value

五、熔断降级:Resilience4J 实战

5.1 雪崩的教训

没接熔断前,stock-service 慢查询导致 order-service 调用超时,order-service 线程池被打满,进而 coupon-service 调 order 也超时,全链路雪崩:
011-cse-microservice-governance-practice_diagram_5.png

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 搞定,微服务后怎么办?
011-cse-microservice-governance-practice_diagram_6.png

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 模式原理
011-cse-microservice-governance-practice_diagram_7.png

踩坑④: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 自动采集:
011-cse-microservice-governance-practice_diagram_8.png

在 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 治理后,三个最大收益:

  1. 故障隔离:通知模块 OOM 不再拖垮下单主链路
  2. 独立演进:5 个团队各自发版,互不阻塞
  3. 精细扩容:大促时只扩 order + stock,不浪费资源扩通知

三个最值得的决策:

  • 业务能力拆而非按团队拆,边界稳定
  • SEATA AT 模式而非 TCC,业务零侵入
  • 治理策略放 CSE 配置中心而非代码里,动态调整不重启

9.2 后续规划

  • 试点 华为云 ASM 服务网格,把熔断/限流/重试从 SDK 下沉到 Sidecar,业务代码零治理
  • 探索 ServiceComb 契约测试,防服务接口变更破坏调用方
  • 接入 CSE 灰度发布,按版本路由流量实现服务级金丝雀

互动引导:你的微服务治理用的是 SDK 模式(Spring Cloud Huawei/Dubbo)还是 Sidecar 模式(Istio/ASM)?各踩过什么坑?欢迎评论区交流。

下一篇拆解 CCE 灰度发布与流量染色,解决"新版本不敢全量发"的痛点,敬请关注。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。