Spring Cloud 从零开始:我的微服务成长之路

清醒开发者
2025-06-14 01:52
阅读 412

引言:为什么选择 Spring Cloud 来构建微服务?

引言:为什么选择 Spring Cloud 来构建微服务?

去年公司接了一个比较大的项目,目标是重构一个已经运行了多年的单体应用。这个老系统是 Java 技术栈写的,业务模块之间耦合严重,部署和上线周期长,每次改动一个小功能都要提心吊胆地回归测试一轮。

随着用户量的快速增长,我们明显感觉到传统单体架构在性能、可维护性以及团队协作方面越来越吃力。技术总监提出要转向微服务架构,并且优先考虑使用 Spring Cloud 去实现。

说实话,当时我内心是有点抵触的 —— 毕竟 Spring Cloud 那么多组件(Eureka、Feign、Gateway、Config、Sleuth…),光是记名字都觉得头疼。再加上没有实际落地的经验,很多问题听起来都知道原理,但真动手时才发现根本不是那么回事。

不过也正是这段经历,让我从最初的懵懂到现在能够独立规划整套微服务架构,也积累了不少实战经验和踩过的坑。这篇文章我就结合自己参与的实际项目,和大家分享一下 Spring Cloud 从零搭建微服务的心路历程。


项目背景:老系统拆分的艰难转型

项目背景:老系统拆分的艰难转型

我们要重构的是一个电商平台的核心系统,包括用户中心、订单系统、商品中心、支付服务等多个模块。原系统代码行数超过百万级,数据库有几百张表,每天处理几万订单。

虽然代码结构混乱,但系统的稳定性要求极高,任何改造都不能影响线上业务的正常运作,因此必须采用灰度方式逐步拆分,而不是推倒重来。

我们在内部成立了“微服攻坚小组”,目标是用半年时间将核心业务拆分为独立的微服务,并基于 Spring Cloud 实现服务注册发现、统一网关路由、链路追踪等功能。


第一阶段:微服务拆分与基础架构搭建

初期挑战:服务怎么拆?粒度太粗 or 太细?

第一个问题就是 服务如何拆分。我们的做法是以业务边界为单位进行划分,比如:

  • 用户相关功能 → user-service
  • 商品管理 → product-service
  • 订单逻辑 → order-service
  • 支付对接 → payment-service

这一步其实是最难的,因为有些功能横跨多个领域,比如库存系统既服务于订单,又服务于商品展示。最后我们决定采用“先大后小”的策略,把通用能力抽出来作为公共服务。

另外还有一个误区就是刚开始就把服务拆得特别细,导致后续维护成本飙升。经验告诉我们:初期保持适中粒度,根据实际负载再进一步细分

技术选型:Spring Boot + Spring Cloud 的组合拳

我们选择的是 Spring Boot + Spring Cloud 的技术栈,理由很简单:

  • 团队熟悉 Java 和 Spring,学习曲线低;
  • 社区活跃,文档齐全;
  • 各个组件配合良好,开箱即用;
  • 真实大规模生产案例众多。

注册中心的选择:Eureka 还是 Nacos?

最初我们选用了 Netflix 的 Eureka 作为服务注册中心,但后来被阿里开源的 Nacos 完美取代。原因有三:

  1. Nacos 自带配置中心功能,省去了额外引入 Spring Cloud Config;
  2. 支持 DNS 和 API 多种服务发现方式,扩展性强;
  3. 可视化控制台更友好,运维同学表示好评。

现在回过头来看,如果项目一开始就使用 Nacos,可能能少走不少弯路

接口设计:RESTful 是基本标准

为了保证各个服务之间通信清晰,我们统一使用 RESTful API,通过 Swagger UI 文档化接口。每新增一个服务,都需要提供一份完整的 OpenAPI Spec,方便前端和其他服务对接。

接口设计上遵循以下几点原则:

  • 路径命名清晰表达语义(例如:GET /api/v1/order/{id});
  • 使用标准 HTTP 方法(GET/POST/PUT/DELETE);
  • 统一错误返回格式,包含 code, message, data
  • 所有请求都经过 Gateway(后面会讲到)做鉴权和限流。

第二阶段:微服务治理能力建设

挑战 1:服务调用怎么保障可靠性?

服务拆分以后,A 调 B 成了常态。最开始我们直接用 RestTemplate 发起调用,结果一旦某个服务慢下来或挂掉,整个调用链就会雪崩式失败。

为了解决这个问题,我们引入了 Feign + Hystrix:

  • Feign 提供声明式客户端调用,简化开发;
  • Hystrix 提供熔断降级机制,保护系统不崩溃。

举个小例子,假设用户服务调用订单服务获取最近订单:

@FeignClient(name = "order-service")
public interface OrderServiceClient {
    @GetMapping("/orders/latest")
    List<Order> getLatestOrders(@RequestParam String userId);
}

然后在调用的地方设置 fallback:

@GetMapping("/user/{userId}")
public UserWithOrders getUserWithOrders(@PathVariable String userId) {
    try {
        return new UserWithOrders(user, orderServiceClient.getLatestOrders(userId));
    } catch (Exception e) {
        // fallback 返回默认值或缓存数据
        return new UserWithOrders(user, Collections.emptyList());
    }
}

不过 Hystrix 已经不再更新了,现在我们正在迁移到 Resilience4j,它更轻量,也更容易集成到 Spring 中。

挑战 2:调用链跟踪怎么做?

微服务环境下,一个用户请求可能会涉及多个服务调用,排查问题变得异常困难。我们采用了 Spring Cloud Sleuth + Zipkin 来解决这个问题:

  • Sleuth 会在每个请求中生成 traceId 和 spanId;
  • Zipkin 负责收集日志并展示完整的调用树。

效果非常明显:以前需要人工查多张日志表才能定位问题,现在只需打开 Zipkin 就能清晰看到整个请求流程、耗时节点。

挑战 3:统一认证怎么设计?

我们采用的是 JWT + OAuth2 方案,在网关层统一做认证鉴权。具体做法如下:

  1. 用户登录后由认证服务生成 JWT Token;
  2. 所有请求都需携带此 Token;
  3. 网关验证 Token 合法性,合法后放行;
  4. 用户信息写入上下文,供下游服务使用。

Token 存储方案起初我们用 Redis 缓存,后来改为无状态的 JWK 签名 + 本地校验,减少依赖项。

挑战 4:分布式事务怎么办?

这是所有微服务绕不开的话题。我们的订单创建过程涉及库存冻结、账户扣款、积分变更等操作,需要保证最终一致性。

目前的做法是:

  • 关键路径使用 Saga 分布式事务模式(自定义补偿机制);
  • 非关键路径采用 异步消息+定时对账
  • 未来考虑引入 Seata 或 RocketMQ 半消息机制。

虽然这些都不是银弹,但在现有业务场景下已经能满足需求。


第三阶段:生产环境运维与性能优化

性能瓶颈分析:别让网关拖累整体性能!

我们使用的 Zuul 一开始表现尚可,但随着服务数量增加,QPS 上不去,CPU 使用率飙高。于是我们果断切到了 Spring Cloud Gateway。

Zuul 是同步阻塞模型,而 Gateway 是基于 Netty 的响应式编程模型,在并发能力上有质的提升。

切换前后性能对比:

框架 QPS(压测) CPU 使用率
Zuul ~800 ~65%
Gateway ~2800 ~30%

建议:尽早使用 Spring Cloud Gateway,避免后期迁移痛苦。

数据库设计:别把锅甩给微服务

服务拆分后数据库也要拆。我们采用了按服务单独数据库 + 最终一致性的方式:

  • 每个服务拥有自己的数据库实例;
  • 不同服务之间的数据交互通过 API 或 MQ;
  • 对于报表类强一致性需求,定期拉取其他服务数据写入本地。

不过这种做法也有代价:需要更多数据库资源,也需要良好的数据同步机制。

我们还做了两件事:

  • 给每张表加上 service_name 字段,用于区分归属;
  • 所有 SQL 都要经过 Review,禁止全表扫描等操作。

日志集中化与监控报警体系

我们使用 ELK(Elasticsearch + Logstash + Kibana)集中化采集日志,并接入了 Prometheus + Grafana 做指标监控。

几个关键指标监控:

  • 请求成功率、响应时间;
  • JVM 内存 GC 情况;
  • 数据库慢查询;
  • 网络连接数、线程池状态;

并且我们设置了告警规则,当某服务错误率超过阈值时自动触发企业微信通知。


技术总结:我的几点经验分享

✅ 推荐做法

  • 不要盲目追求服务拆分数量,而是以业务职责为核心
  • 使用 Spring Cloud Gateway 替代 Zuul,性能更好
  • 尽早接入服务链路追踪(Sleuth + Zipkin)
  • 采用统一网关做身份认证,避免重复劳动
  • 数据库拆分时预留中间层或同步机制,应对复杂查询场景

❌ 常见陷阱

  • 过度依赖 Ribbon + RestTemplate,不如直接使用 Feign
  • 忽视服务容错,导致系统稳定性差
  • 前期没想好服务边界,后期拆分痛苦
  • 不做日志集中化和指标监控,出了问题只能靠祈祷

📦 生产注意事项

  • 所有微服务必须有健康检查端点(如 /actuator/health);
  • 配置文件尽量使用 Nacos 管理,动态热更新很重要;
  • 微服务之间尽量避免循环依赖;
  • 所有服务部署在同一集群,降低网络延迟影响;
  • 定期清理服务注册列表,防止僵尸服务堆积。

写在最后:微服务不是终点,而是新的起点

回顾这一年的微服务演进,最大的感受就是 微服务从来不是一个“银弹”解决方案,而是一场组织、技术、认知的全面升级

Spring Cloud 提供了非常棒的基础组件,但也正因为其强大的灵活性,很容易让人陷入“组件依赖症”或者“过度设计”的陷阱。我们需要始终记住:

微服务的本质在于解耦,而非拆分本身。

现在的我们还在探索服务网格(Istio)、容器化编排(K8s)的路上继续前行,希望有一天能把这套体系更加云原生地运行起来。

如果你也在微服务这条路上迷茫,不妨从最简单的几个服务开始,慢慢摸索出属于自己的“最佳实践”。

共勉。


如果你感兴趣的话,我可以后续继续分享我们在服务网格、分布式事务、灰度发布等方面的实战经验,欢迎留言讨论~

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝