Spring Cloud 从零开始:微服务入门指南 —— 我的实战成长笔记
引言:一次重构让我和 Spring Cloud 真正结缘

去年年初,我所在的公司决定对一个运行多年的单体系统进行拆分。这个系统原本是一个基于 Spring Boot 构建的“大而全”的项目,模块之间通过类依赖强耦合在一起,部署时也是一起打包上线。随着用户量的增长和功能迭代的频率提升,团队逐渐感受到维护成本飙升、发布风险陡增。
作为后端技术负责人之一,我也参与了这次架构升级的全过程。我们最终选择了 Spring Cloud 来构建我们的微服务架构。说实话,刚开始我对这套体系只有一知半解,但在实践中踩了很多坑之后,渐渐体会到了它的威力和复杂度。
今天我就想结合那次项目的经历,来分享一下我是如何从零开始上手 Spring Cloud,搭建起一套基础可用的微服务架构的。如果你刚接触这个框架,或者正在准备踏上微服务之路,这篇文章或许能给你一些参考。
项目背景 & 遇到的问题

我们原来的系统是典型的单体应用,所有逻辑都写在一个项目里:
- 模块划分靠 package(package com.xxx.user; package com.xxx.order;)
- 数据库共用一个 schema,表与表之间依赖很多
- 功能迭代需要整体重新编译部署,影响面很大
- 新人上手慢,因为要理解整个系统的结构和依赖关系
于是公司决定启动微服务重构计划,目标是将不同业务模块拆分为独立服务,并实现服务之间的注册、发现、负载均衡、通信、容错等能力。Spring Cloud 成为了首选的技术栈,主要原因有几点:
- 基于我们已有的 Java 技术栈,学习曲线相对平缓;
- 社区活跃、生态完善,Netflix、Alibaba 等都有成熟的组件;
- 对接云原生支持良好,后续容器化也比较顺畅。
但我们也不是一上来就高歌猛进。而是先从一个小模块开始试点,逐步验证架构的可行性和可扩展性。
实施过程:一步步搭建微服务骨架

我们以用户中心和订单中心两个模块为第一批拆分对象。这两个模块之间有一定的交互,比如下单的时候需要调用用户信息接口查询余额。
第一步:选型 + 搭建基础设施
我们选用的 Spring Cloud 栈如下:
- 服务注册中心:Eureka
- 服务调用:RestTemplate + Ribbon 或 OpenFeign
- 配置中心:Spring Cloud Config(后来改成了阿里 Nacos,下文会提到)
- 熔断降级:Hystrix(后来换成 Sentinel)
- 网关:Zuul(后面也换成了 Gateway)
小插曲:一开始我们尝试用了 Eureka,结果部署在测试环境总是注册不上,最后查出来是因为主机名解析有问题。后来在 Kubernetes 上跑就没这问题了,看来本地开发还是要多注意网络设置。
第二步:创建独立的服务工程
这里就不赘述每个服务的具体拆分策略了,总之我们做了数据库分离,各自使用自己的数据源。然后就是创建 Maven 工程,引入对应的依赖。
以用户中心为例,pom.xml 中关键依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 其他如数据库、MyBatis、日志等等省略 -->
</dependencies>
然后配置 application.yml:
server:
port: 8081
spring:
application:
name: user-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
这样服务启动的时候就会自动注册到 Eureka 上。
第三步:服务间调用
接下来就需要让订单服务去调用用户服务。我们当时采用了 Feign 的方式,写一个客户端接口:
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{userId}")
UserInfo getUserById(@PathVariable Long userId);
}
配合 @EnableFeignClients 注解开启 Feign 支持。Ribbon 会自动做负载均衡,选择一个可用的 user-service 实例发送请求。
不过我们也遇到了一个问题:有时候 Feign 调用没有超时控制,导致整个链路卡住。后来我们就加了 Hystrix 进行熔断兜底。
第四步:添加熔断机制
加上 Hystrix 后,我们在 Feign 接口的方法中增加了 fallback:
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
...
}
@Component
public class UserClientFallback implements UserClient {
public UserInfo getUserById(Long userId) {
return new UserInfo();
}
}
同时在配置文件中启用 Hystrix:
feign:
hystrix:
enabled: true
虽然 Hystrix 功能很强大,但社区已经进入维护模式,我们后面也换成了阿里的 Sentinel,在生产环境中使用更稳定。
第五步:配置管理
最初我们用了 Spring Cloud Config,把所有的配置放在 Git 仓库中统一管理。但由于我们内部已经有自研的配置平台,后期改为直接对接 Nacos。
在 bootstrap.yml 中指定 Nacos 地址:
spring:
cloud:
nacos:
config:
server-addr: 192.168.1.10:8848
file-extension: yaml
这样就可以在 Nacos 上集中管理配置,避免散落在各个服务中难以维护。
第六步:引入网关层(API Gateway)
我们选用 Zuul 做 API 网关,统一处理路由、鉴权、限流等功能。后来切换到 Spring Cloud Gateway,性能更好,功能更强。
网关的核心配置大致如下:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
这样,外部请求只需要访问 /api/user/xxx 和 /api/order/xxx,由网关负责转发给对应的服务。
踩过的坑和解决经验

在整个实践过程中,我们踩了不少坑,有些是配置问题,有些是设计不合理。
1. 微服务粒度太细,造成调用链过长
初期为了“看起来规范”,我们把每个小模块都拆成独立服务。结果一个请求要经过五六次服务间调用,响应时间飙到秒级。
教训:
- 微服务粒度应根据业务边界合理划分,不要盲目追求“一切皆服务”
- 同一领域内尽量合并服务,减少远程调用次数
2. 忘记考虑幂等和异步补偿
微服务环境下,由于网络的不确定性,必须做好重试机制,否则容易导致重复操作(比如支付、积分增加)。
改进方法:
- 所有写操作都要带业务唯一标识(如订单号、流水号)
- 使用幂等拦截器,记录请求指纹
- 关键路径走消息队列+状态机做事务补偿
3. 网络延迟和超时未设置好
刚开始没设置合适的超时时间和重试次数,导致某个服务故障引发连锁雪崩效应。
解决办法:
- 统一制定超时策略(例如:Feign 默认 1s)
- 设置合理的重试次数(一般是失败一次不重试,或重试最多一次)
- 结合 Sentinel 做服务降级,在高峰期主动关闭非核心功能
4. 日志追踪缺失,排查困难
最开始所有服务的日志都是独立的,出了问题根本不知道从哪查起。
后来我们接入了 Sleuth + Zipkin 做分布式链路追踪:
- 在每个服务中添加 Sleuth 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
- 配置 Zipkin Server,采集 Span 信息
这样每次请求都会带上 Trace ID,大大提升了定位问题的效率。
最终效果和收获
经过半年多的努力,我们完成了初步的服务拆分,并实现了如下能力:
- 服务注册发现 ✔️
- 服务间通信(Feign/Ribbon)✔️
- 熔断降级(Sentinel)✔️
- 分布式配置管理(Nacos)✔️
- API 网关(Gateway)✔️
- 链路追踪(Sleuth + Zipkin)✔️
收益也是显而易见的:
- 部署灵活:每次只需发布变更的服务,不影响其他模块;
- 弹性扩容:可以根据流量自动水平伸缩热点服务;
- 容错性增强:即使某服务异常,也不会整站崩溃;
- 多人协作顺畅:不同团队专注自己负责的服务,降低耦合;
- 稳定性提升:配合监控报警系统,可以及时发现异常点。
当然,代价也不小:
- 学习成本高,新人上手慢;
- 测试难度变大,联调成本上升;
- 复杂场景下维护分布式事务非常麻烦;
- 需要投入精力做运维支持。
给新手的一些建议和注意事项
如果你刚开始接触 Spring Cloud,我想结合自己的经验,给你一些建议:
1. 不要一开始就追求“完美架构”
刚开始的时候,很多人想着“我要做一个完美的微服务架构”,包括各种熔断、监控、安全、权限等等。其实没必要。
我的建议是先从最小集入手,先把服务注册、发现、调用跑起来再说。后面的高级功能可以用哪个补哪个。
2. 把控好服务拆分的粒度
不要过度拆分服务,尤其是业务关系密切的模块。建议遵循以下原则:
- 服务自治,职责单一
- 数据隔离,避免共享数据库
- 服务间通过标准接口通信
- 保持服务大小可控,方便维护
3. 接口设计要考虑幂等、健壮和演进
服务间的通信本质上是一种契约,一旦暴露出去,就很难更改。
所以设计接口时要考虑到:
- 请求参数简洁清晰,文档齐全;
- 返回值结构统一,便于识别;
- 增加版本号,支持接口演进;
- 加入限流、降级、熔断机制。
4. 监控不能少,日志一定要统一
分布式环境下,监控和日志的重要性远高于单体系统。
建议你尽早接入:
- 应用健康检查(actuator)
- 请求耗时统计(Micrometer + Prometheus)
- 日志聚合(ELK)
- 链路追踪(Sleuth + Zipkin)
这些工具会在你遇到线上问题的时候救你一命。
5. 生产环境要用配置中心,别写死配置
不要把 DB 配置、Redis 地址、开关变量等写在代码里或者配置文件中。统一放到配置中心管理,方便动态调整。
我们早期就是犯了这个错误,改一个开关都要发版,现在想想都痛心。
写在最后:微服务不是银弹,但它值得你认真对待
从零开始学习 Spring Cloud,对我来说是一段充满挑战但也非常充实的经历。它不仅让我掌握了更多架构层面的知识,也让我学会了从更高视角去思考系统的可扩展性和稳定性。
如果你问我:“要不要开始微服务?”
我会反问你几个问题:
- 是否有足够的技术积累和运维能力?
- 团队能否接受更高的协作成本?
- 当前系统是否真的到了单体无法支撑的地步?
如果不是特别大的流量压力或者复杂的业务耦合,不一定非要上微服务。但如果真有需要,而且你能接受它带来的复杂性,那么 Spring Cloud 是一个非常好的起点。
希望这篇文章能帮助你少走弯路,也希望你在学习微服务的路上越走越远。我们一起加油!如果你有任何疑问或建议,欢迎留言交流 🙌

评论 0