从零开始搭建Spring Cloud微服务:我的实战笔记
开篇 · 背景与动机

大家好,我是一个在后端开发岗位摸爬滚打了五年的程序员。之前做过传统的单体架构项目,也参与过多个大型分布式系统的设计和实现。这两年,公司业务规模不断增长,原有的单体架构越来越难支撑日益复杂的业务需求。于是,我们决定进行一次大刀阔斧的架构升级——引入Spring Cloud搭建微服务架构。
这篇文章是我从0到1搭建Spring Cloud微服务的真实经历,过程中踩了不少坑,但也积累了很多经验。希望通过这篇总结性的文章,能帮你少走弯路,更快地上手Spring Cloud这套体系。
初遇微服务:为什么我们需要它?

我们当时负责的是一个电商平台的后端系统,主要模块包括用户管理、商品中心、订单处理、支付、库存管理等。最开始的时候是单体应用,所有逻辑都放在一个项目里。随着业务扩展:
- 接口调用越来越多,代码耦合严重
- 每次发布都要全量部署,风险高
- 系统性能瓶颈逐渐显现
- 不同团队之间协作困难,改动容易冲突
这些问题导致每次上线都需要全员加班review代码,维护成本非常高。于是我们开始考虑拆分服务,构建一个真正解耦、可扩展的系统架构。微服务成了我们的首选方案,而Spring Cloud正好是一套完整的微服务解决方案。
Spring Cloud初探:技术选型的那些事


我们在做技术选型时,其实也有对比其他方案,比如Dubbo + Zookeeper组合。但在当时看来(现在可能略有不同),Spring Cloud更适合我们这样的Java团队:
| 技术栈 | Dubbo + Zookeeper | Spring Cloud |
|---|---|---|
| 服务注册发现 | Zookeeper | Eureka / Nacos |
| 通信方式 | RPC | HTTP REST |
| 配置管理 | 外部集成 | Spring Cloud Config |
| 链路追踪 | 需要自己集成 | Sleuth + Zipkin |
| 熔断机制 | Hystrix(已停更) | Hystrix / Resilience4j |
| 学习曲线 | 相对平缓 | 对Spring开发者友好 |
最终,结合项目背景以及我们现有的Spring Boot基础,我们选择了Spring Cloud Alibaba的技术栈,主要原因有:
- 与原项目的兼容性更好
- Nacos比Eureka功能更强大
- 支持灰度发布、配置中心等功能
- 社区活跃,文档丰富
架构设计:从哪里开始拆?

第一步:明确服务边界
这是我们最容易忽视但又最重要的一步。一开始我们也犯了“为了微服务而微服务”的错误,盲目拆分模块,结果反而带来更大的复杂度。
后来我们明确了几个原则:
- 领域驱动设计(DDD):按照业务功能划分服务边界
- 单一职责:每个服务只做一件事,不相互依赖核心逻辑
- 高内聚低耦合:尽量减少跨服务调用
所以我们最终划分为这几个核心服务:
| 服务名 | 功能说明 | 数据库独立? | 使用场景 |
|---|---|---|---|
| user-service | 用户账号管理、权限控制 | ✅ | 登录、注册、权限控制 |
| product-service | 商品信息管理、分类管理、库存同步 | ✅ | 商品展示、搜索、库存扣减 |
| order-service | 订单创建、状态变更、结算逻辑 | ✅ | 下单、支付、售后 |
| payment-service | 支付流程处理、第三方接口封装 | ✅ | 异步回调、支付状态同步 |
| gateway | 统一API入口、鉴权、路由转发 | ❌ | 前端统一接入 |
| config-center | 配置中心 | ❌ | 所有服务的全局配置加载 |
小插曲:刚开始尝试把订单拆得非常细,比如订单主表服务、明细服务、地址服务……结果上线后调用链太复杂,调试困难,最后只能合并回order-service。
第二步:搭建基础设施
服务注册与发现 - Nacos
Nacos作为我们的注册中心,支持服务的注册、发现、配置管理三大功能。相比Eureka来说,它的可视化界面和动态配置刷新能力让我们运维起来轻松不少。
# 示例:在product-service中配置Nacos注册
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
启动服务之后,在Nacos后台可以看到服务列表。
实战小技巧:服务实例的心跳周期默认为5秒,我们可以根据生产环境调整
nacos.client.heartbeat.interval来优化负载。
API网关 - Gateway + JWT鉴权
我们使用Spring Cloud Gateway作为统一网关入口,配合JWT完成身份认证和权限校验。
// 示例:定义一个全局过滤器用于检查token
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(token)) {
// 返回401
}
boolean isValid = verifyToken(token); // 校验逻辑
if (!isValid) {
// 返回403
}
return chain.filter(exchange);
}
}
同时,我们在gateway层做了限流策略,使用Redis+令牌桶算法限制每分钟访问频率,避免突发流量压垮下游服务。
分布式配置中心 - Nacos Config
将所有服务的配置抽离出去,通过Nacos集中管理:
# 示例:配置自动刷新
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
extension-configs:
- data-id: product.yaml
group: DEFAULT_GROUP
refresh: true
这样在后期运维时只需修改Nacos中的配置文件,即可动态生效,极大提高了灵活性。
链路追踪 - Sleuth + Zipkin
在微服务环境下,定位问题变得特别复杂。我们引入了Sleuth生成日志链路ID,并将其上报给Zipkin进行分析。
# 示例:启用Sleuth和Zipkin上报
spring:
zipkin:
base-url: http://localhost:9411/
sleuth:
sampler:
probability: 1.0 # 100%采样率(测试环境)
有了这个工具以后,我们可以通过traceId快速串联起整个请求链路,查哪一步耗时、有没有失败节点都很清晰。
一次线上排查经历:用户反馈下单慢,通过Zipkin发现原来是库存服务调用超时,进而触发重试导致雪崩效应。修复后效率提升显著。
微服务间通信:RestClient还是Feign?
我们一开始使用的是Feign,简单易用,适合HTTP调用:
@FeignClient(name = "product-service")
public interface ProductServiceClient {
@GetMapping("/products/{id}")
Product getProductById(@PathVariable("id") Long id);
}
但随着业务量增加,Feign暴露出了几个问题:
- 默认没有连接池,性能有限
- 对异常处理不够灵活
- 有时候会因为序列化失败导致数据错乱
后来我们将部分关键调用切换到了RestTemplate + 自定义拦截器的方式,并整合了Ribbon做客户端负载均衡:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Autowired
private RestTemplate restTemplate;
public Product getProduct(Long id) {
return restTemplate.getForObject("http://product-service/products/" + id, Product.class);
}
虽然代码稍微麻烦一点,但是性能更好,而且可以更细粒度地控制请求参数、异常处理逻辑等。
小建议:如果你对性能要求不高的场景,推荐使用Feign;如果追求稳定性和性能,可以考虑Apache HttpClient或OkHttp封装。
分布式事务:怎么保证数据一致性?
这是我们在订单模块遇到的最大难题之一。订单创建时需要:
- 减库存 → product-service
- 创建订单记录 → order-service
- 扣用户积分 → user-service
这三个操作必须要么全成功,要么全失败。我们调研了几个方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| Seata分布式事务框架 | 提供AT模式,类似本地事务 | 对数据库和版本要求较高,学习成本略高 |
| 最终一致性方案 | 实现简单,适用于异步场景 | 有可能出现短暂数据不一致 |
| 消息队列兜底补偿 | 解耦性强 | 业务逻辑复杂,需要额外监听服务 |
最终我们采用了基于事件驱动的最终一致性方案,即当某个服务出错时,通过消息队列(如RocketMQ)进行补偿处理。
举个例子:当库存服务调用失败,则发一条延迟消息,等待一段时间后重试;若多次失败则通知人工介入。
这种方式虽然不能完全做到强一致性,但在实际业务中已经足够,同时也避免了分布式事务带来的锁竞争问题。
个人心得:不要一上来就追求完美的一致性方案,先评估你的业务是否真的需要强一致性,有时候“容忍短暂不一致”是更优的选择。
数据库设计:服务之间如何共用数据?
刚开始的时候我们遇到了一个很头疼的问题:订单服务需要用户的手机号,怎么办?直接调user-service接口肯定是最简单的方式,但一旦调用失败,整个流程就会卡住。
经过讨论,我们采取了一些折中做法:
- 冗余字段:订单表中存储用户的快照信息,如昵称、手机号、地址等
- 定期同步:每天凌晨跑定时任务更新快照数据
- 读写分离:对于一些非实时的数据,允许一定程度延迟
这样做虽然牺牲了一定的数据新鲜度,但大大提升了系统的可用性和响应速度。
另外,为了防止服务之间的环形依赖,我们制定了一个规则:禁止两个服务互相远程调用。如果确实需要双向通信,优先通过消息队列解耦。
性能优化和线上踩坑实录
服务启动慢?Nacos心跳间隔调短
我们在压测的时候发现,有些服务启动后需要几十秒才能被Nacos识别。经查发现是由于Nacos默认心跳时间较久(约5秒一次),可以通过修改以下参数缩短:
spring.cloud.nacos.discovery.heartbeat.interval: 3000 # 心跳间隔3s
这在灰度发布或者弹性扩缩容时非常重要。
接口响应慢?别忘了Feign的连接超时设置
feign:
client:
config:
default:
connectTimeout: 2000 # 连接超时时间
readTimeout: 5000 # 读取超时时间
Feign默认没有配置超时,很容易在某些网络波动时拖慢整个链路。
日志分散?ELK集中收集日志
我们搭建了ELK(Elasticsearch + Logstash + Kibana)来做日志集中采集与检索。每个服务输出的日志都被Logstash抓取,然后上传到Elasticsearch中,方便我们快速定位问题。
Tips:记得在每条日志中添加traceId,可以在Kibana中按trace筛选日志,这对分析调用链超级有用!
容灾演练:熔断与降级不能少
我们一开始没上Hystrix,某天用户服务挂了,导致整个下单链路全部阻塞。后来加上了熔断策略,一旦某个服务调用失败超过阈值,就返回预设的默认值:
@HystrixCommand(fallbackMethod = "getDefaultUserInfo")
public UserInfo getUserInfo(String userId) {
// ...
}
public UserInfo getDefaultUserInfo(String userId) {
return new UserInfo(userId, "default_user", "未知");
}
此外,我们还会定期做故障演练,模拟服务不可用、网络分区、数据库宕机等情况,以验证系统的健壮性。
效果总结:微服务给我们带来了什么?
自从全面拥抱Spring Cloud微服务以来,我们的系统架构发生了质的变化:
✅ 上线效率提升:各个服务可以独立部署,不再受整体影响
✅ 运维更可控:每个服务都能独立监控、扩容
✅ 团队协作顺畅:不同小组负责不同服务,互不影响
✅ 稳定性增强:通过熔断、限流、日志追踪等手段降低故障影响范围
特别是在双十一大促期间,整个系统扛住了几倍于日常的压力,没有出现大规模故障,这是传统架构难以想象的。
我的经验建议:给刚入门的同学几点提醒
🚨 1. 先做好单体架构,再拆微服务
很多人一上来就想拆微服务,但实际上如果没有良好的代码结构和清晰的服务边界,拆出来的只是“分布式的屎山”。
建议:
- 先梳理好业务模块之间的关系
- 在单体中做好模块隔离,比如Maven分模块
- 再逐步拆出高价值的微服务
🚨 2. 不要盲目追求新技术
Spring Cloud生态非常庞大,很多组件刚出来的时候我们觉得很酷,都想试试,结果反而增加了理解和运维成本。
建议:
- 明确当前阶段的核心需求
- 优先选择成熟稳定的组件
- 逐步引入高级特性
🚨 3. 日常开发要注意“服务意识”
每个开发人员都要具备一定的微服务思维,比如:
- 设计接口时要考虑幂等性
- 写SQL时避免关联多个服务的数据
- 理解异步和同步调用的区别
只有每个人都从服务化的角度去思考问题,整套架构才能真正发挥价值。
结尾 · 写在最后
说到底,Spring Cloud并不是银弹。微服务也不是万能药,它解决了一些问题,也会带来新的挑战。关键还是要理解自己的业务,找到合适的切入点。
希望我这些真实的经历和教训,能够帮助你顺利走上微服务之路。如果你正在搭建微服务架构,欢迎留言交流,我们可以一起踩坑、一起填坑 😊
祝大家编码愉快,少改需求,多涨工资!

评论 0