从零开始搭建 Spring Cloud 微服务:我的实战心得
背景与动机

去年年初,我所在的团队接到了一个全新的项目需求:为一家中型连锁零售企业重构其电商平台。原本系统是一个庞大的单体架构,代码量超过百万行,部署复杂,每次发版都要小心翼翼,动辄牵一发而动全身。客户希望我们用微服务架构来拆分业务模块,提升系统的可维护性和扩展性,并为后续的云原生演进打下基础。
虽然之前在别的项目里接触过 Spring Boot 和部分 Spring Cloud 组件,但真正从零开始构建一套完整的微服务体系,对我们来说还是一次不小的挑战。特别是要考虑到未来可能会迁移到 Kubernetes 上做容器化部署,技术选型和架构设计都必须有前瞻性。这篇文章想通过我亲身经历的这段旅程,结合实际项目中的踩坑和收获,给大家分享一下我是如何一步步搭建起这套 Spring Cloud 微服务体系的。
问题描述:单体架构的痛点与微服务带来的新挑战


单体应用的问题已经非常明显:
- 发布风险大:所有功能都在同一个应用中,改一个小功能也要全量部署。
- 代码臃肿:模块间耦合严重,修改一处可能影响到其他模块。
- 性能瓶颈:部分核心接口并发高、响应慢,整体系统资源浪费严重。
- 横向扩展困难:不同模块对资源的需求不同,却只能一起部署。
于是我们决定采用 Spring Cloud 构建微服务架构,按业务领域划分服务。理想很美好,现实却不简单。我们面临几个关键问题:
- 服务如何注册发现?Eureka 还是 Nacos?
- 服务调用怎么保证可靠性?Ribbon + Feign 是否足够?
- 配置如何统一管理?Spring Cloud Config 还是更灵活的方案?
- 服务异常如何快速定位?需要接入链路追踪吗?
- 数据库拆分怎么做?是否需要分布式事务?
- 生产环境如何部署?要不要用 Kubernetes?
这些问题都需要一一解决。而且由于是真实商业项目,每一个选择都有成本考量。容不得我们反复试错。接下来我就按照我们的建设步骤,来详细聊聊我们是怎么一步步推进的。
技术方案设计与落地实践

第一步:基础设施准备
我们选择了 Spring Boot + Spring Cloud Alibaba(基于 Hoxton.SR10),主要是考虑未来如果上阿里云,可以无缝接入阿里的一些中间件服务。
服务注册与发现:Nacos 替代 Eureka
一开始我们尝试用了 Eureka,但随着服务实例数量的增加,心跳检测频繁导致 GC 压力大,节点挂掉时经常出现“假死”状态。后来换成了 Nacos,支持 AP + CP 混合模式,注册信息更新更快、感知也更及时。尤其当我们使用 Sentinel 做限流降级时,Nacos 的健康检查机制非常有用。
# application.yml 示例
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.10.10:8848
接口通信:Feign + LoadBalancer + Sentinel
我们在各个微服务之间统一使用 Feign 来做声明式调用,并配合 LoadBalancer 实现客户端负载均衡。同时为了应对突发流量和服务降级,我们引入了 Sentinel,并结合 OpenFeign 的 fallback 实现优雅降级。
举个例子,库存服务在高峰期压力很大,我们就在商品中心调用它的时候做了熔断策略:
@FeignClient(name = "inventory-service", fallback = InventoryServiceFallback.class)
public interface InventoryServiceClient {
@GetMapping("/api/inventory")
ResponseEntity<Integer> getStock(@RequestParam String productId);
}
配置中心:Spring Cloud Alibaba Config + Nacos
传统的 Spring Cloud Config + Git 方案更新不够实时,所以我们选择了基于 Nacos 的配置中心。每个服务启动时都会去拉取自己的配置,比如日志级别、限流规则等。
# bootstrap.yml
spring:
cloud:
config:
extension:
enabled: true
nacos:
server-addr: 192.168.10.10:8848
namespace: prod-env
file-extension: yaml
这极大地简化了配置管理,特别是在多环境部署的时候,再也不用手动复制粘贴配置文件了。
日志与监控:ELK + Prometheus + Grafana
我们用 ELK 收集日志,在 Kibana 中搜索日志排障;Prometheus 拉取服务指标,Grafana 做可视化展示。另外,我们也接入了 SkyWalking 做链路追踪,帮助排查接口延迟和调用链异常。
这部分工作其实是最耗时间的,尤其是初期没有标准化的日志格式,排查日志就像大海捞针。后来我们制定了一套统一的日志规范,包括 traceId、请求路径、用户ID等信息。
数据库拆分:垂直切分 + 分库分表初步规划
为了防止后期数据膨胀拖垮系统,我们在数据库层面也开始逐步拆分。将会员、订单、支付等模块分别独立成各自的数据库。同时我们评估后认为当前阶段还不需要引入 TCC 或 Seata 等复杂的分布式事务方案,采用最终一致性处理跨服务的事务场景。
例如下单逻辑,涉及订单、库存、优惠券等多个服务,我们就采用了异步消息队列来协调状态同步:
// 订单创建成功后发送消息到 RocketMQ
rocketMQTemplate.convertAndSend("ORDER_CREATED_TOPIC", orderDto);
// 库存服务监听该消息消费
@RocketMQMessageListener(topic = "ORDER_CREATED_TOPIC", consumerGroup = "INVENTORY_GROUP")
public class OrderCreatedConsumer implements RocketMQListener<OrderDto> {
@Override
public void onMessage(OrderDto message) {
inventoryService.decrease(message.getProductId(), message.getCount());
}
}
这样解耦了服务之间的强依赖,提升了系统的可用性。
工程结构与开发规范

为了让多个团队协作顺利进行,我们制定了统一的工程结构标准:
project/
├── common/
│ ├── utils/
│ └── exception/
├── gateway/
│ └── GatewayApplication.java
├── auth-service/
├── product-service/
├── order-service/
└── inventory-service/
此外,我们还在公共包中封装了一些通用的工具类、自定义异常处理、响应包装器等,避免重复造轮子。
在接口设计方面,我们坚持 RESTful 风格,并统一返回格式:
{
"code": 200,
"message": "success",
"data": {}
}
对于一些敏感字段(如身份证号、手机号),我们还在全局拦截器中进行了脱敏处理,保障信息安全。
生产运维经验分享
上线后我们遇到不少运维相关的问题,这里分享几点:
1. 服务实例频繁上下线?
早期版本的服务偶尔会因为 JVM 内存不足或线程阻塞导致被 Nacos 标记为不健康。我们做了以下优化:
- 合理设置 JVM 参数,启用 Native Memory Tracking 分析非堆内存泄漏
- 增加健康检查接口
/actuator/health,细化健康判断逻辑 - 设置合理的超时重试和熔断阈值,避免雪崩效应
2. 大促期间接口响应慢?
我们分析链路跟踪数据发现,很多接口其实是卡在数据库访问上。为此,我们做了如下优化:
- 对热点数据做了缓存(Redis)
- 在商品详情页引入 CDN 缓存静态内容
- 对数据库做了读写分离(主从架构)
- 慢查询加索引、调整执行计划
3. 日志爆炸,定位效率低下?
刚开始大家随便打印日志,后来我们制定了日志分级标准,强制要求:
- Info 级别用于流程标记和常规操作
- Debug 仅用于本地调试,线上关闭
- Error 必须包含错误上下文,便于回溯
- 所有日志都要带上 traceId、requestId 等唯一标识
并且日志平台做了索引优化,让搜索速度提升了不少。
实施效果总结
经过三个月的努力,我们的系统发生了显著变化:
| 指标 | 上线前 | 上线后 |
|---|---|---|
| 接口平均响应时间 | 800ms | 320ms |
| 故障恢复时间 | 1小时+ | <10分钟 |
| 发布频率 | 月度 | 双周迭代 |
| 单服务故障影响范围 | 全站崩溃 | 局部不可用 |
| 团队协作效率 | 开发互相等待 | 并行开发 |
最明显的是,我们现在可以按服务维度做弹性扩容,而不是整个系统一块扩。这大大节省了云资源成本,也为未来接入 Kubernetes 做好了铺垫。
我的经验建议
如果你正准备从头开始搭建一套 Spring Cloud 微服务架构,我可以给你几个实用建议:
✅ 不要一开始就追求大而全的解决方案
很多人一上来就要集成 Spring Cloud 全家桶,把服务网关、认证中心、分布式事务全都加上。其实前期只需要:
- 服务注册发现(Nacos)
- 远程调用(Feign + LoadBalancer)
- 配置中心(Nacos Config)
- 日志和基本的监控(ELK + Prometheus)
其他的像 Sentinel、Sleuth、Zipkin、Seata 这些都可以随着业务发展逐步加入。
✅ 服务粒度不要太细,避免过度拆分
很多新手一上来就想把每个功能都拆成服务,结果最后服务太多,管理和维护反而成了负担。建议:
- 初期按业务域划分服务,比如:用户服务、订单服务、商品服务
- 后续再根据实际情况进一步拆分(如支付、库存)
- 避免单个服务职责太单一,也不要糅合太多业务进去
✅ 提前设计好统一的开发规范和文档标准
多人协作的关键在于一致的编码风格和文档规范。我们项目组做的几件事:
- 使用 Swagger 自动生成 API 文档
- 制定 RESTful 接口命名规则(如
/orders/{id}而不是/order.do?method=getOrderById) - 推行 GitFlow 分支管理模型
- 强制 PR 审核和自动化测试覆盖
这些规范看似繁琐,实际上减少了沟通成本,提升了协作效率。
✅ 性能设计不能忽视
微服务虽然解决了功能拆分的问题,但也带来了额外的网络开销和潜在故障点。所以在设计阶段就应该考虑:
- 服务间的通信方式:是否引入 gRPC?是否需要协议转换?
- 数据库连接池大小:Too many connections 是高频故障
- 缓存策略:哪些数据适合缓存?TTL 如何设置?
- 压测和容量评估:提前识别瓶颈点
✅ 保留一定的技术债务空间
任何架构都不可能是完美的,尤其在交付压力大的项目里,总有些事情来不及优化。我们当时也做了妥协,比如:
- 先用 Kafka 做事件通知,后面再过渡到更复杂的 Saga 模式
- 有些服务先共用一个数据库,等业务稳定后再拆库
- 某些非核心接口允许一定程度的数据不一致
关键是你要清楚这些地方是技术债,要记得回头还。
结语:技术只是手段,价值才是目标
回顾整个项目的历程,我越来越体会到一句话:“架构服务于业务”。Spring Cloud 提供了一套强大的组件生态,但它终究只是工具。真正的难点在于:
- 如何合理划分服务边界?
- 如何权衡一致性与可用性?
- 如何平衡交付压力和技术债务?
每家公司所处的阶段不同,面临的挑战也不一样。希望我这篇亲历的文章,能够为你带来一点启发。如果你正在尝试搭建自己的微服务系统,不妨留言交流你的困惑和实践经验,我很乐意一起探讨。
毕竟,在这个持续演进的时代,只有不断学习和分享,才能走得更远。

评论 0