从零开始搭建Spring Cloud微服务:一位后端工程师的实战分享
背景介绍

大家好,我是一名有五年开发经验的后端工程师。在过去几年里,我在多个项目中深度参与了微服务架构的设计与落地。今天想和大家分享的是我们团队在一个电商平台重构过程中,如何从零开始一步步搭建基于 Spring Cloud 的微服务架构体系的真实经历。
这次项目的背景是一个中型电商系统,原本是单体结构,随着业务量增长,出现了接口响应慢、部署频率低、维护成本高、新功能迭代困难等问题。于是公司决定将系统拆分为多个独立的服务,采用微服务架构来解耦各业务模块,并提升可扩展性和可维护性。
遇到的挑战

刚开始接触微服务时,说实话我还是挺懵的。虽然以前在学习和Demo项目中了解过 Spring Cloud,但真正在实际工作中上手的时候才发现,它远不是“加个@EnableEurekaServer”这么简单。我们遇到的主要问题包括:
- 服务注册与发现:多个服务如何快速找到彼此?
- 负载均衡:用户请求如何平均分配给多个实例?
- 配置管理:不同环境下的配置文件如何集中管理?
- 分布式事务:用户下单时库存扣减和订单创建如何保证一致性?
- 日志追踪:一个请求跨多个服务,怎么定位问题?
- 部署运维复杂度陡增:本地跑得好好的,线上出问题怎么办?
这些看似是技术细节,实则是微服务落地中最基础也最关键的点。
解决方案:从零构建Spring Cloud微服务架构

第一阶段:搭起骨架 —— 注册中心 + 网关 + 基础服务
我们首先使用 Eureka 做服务注册中心,所有服务启动时都向 Eureka 注册自己的信息(IP、端口、健康状态等),其他服务通过 Eureka 获取目标服务地址。
然后引入 Zuul 作为 API 网关,统一接收外部请求,并根据路由规则转发到对应的业务服务。这不仅统一了入口,也方便后续添加限流、鉴权等功能。
初期我们只拆分了三个基础服务:
- 用户服务(User Service)
- 商品服务(Product Service)
- 订单服务(Order Service)
每个服务都是独立的 Spring Boot 应用,拥有自己的数据库表结构,服务之间通过 RESTful API 通信。
举个例子,在商品详情页需要展示用户是否已收藏该商品。这个时候,前端调用网关 /product/detail 接口,网关会转调 Product Service,而 Product Service 再调用 User Service 的 /user/favorite?productId=xxx 来判断收藏状态。
这种调用关系一开始还可以接受,但随着服务数量增加,问题就来了。
第二阶段:引入 Feign 和 Ribbon 实现客户端负载均衡
我们最初直接使用 RestTemplate 来发起 HTTP 请求,但在服务实例增多之后发现一个问题:如果某台机器上的 User Service 挂了,请求还是会发过去,导致超时甚至失败。
后来我们切换为 Feign + Ribbon,这样就可以实现客户端的负载均衡。Feign 本质上是对 RestTemplate 的封装,提供声明式调用接口:
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/user/favorite")
Boolean isFavorite(@RequestParam("userId") Long userId,
@RequestParam("productId") Long productId);
}
Ribbon 则负责从 Eureka 获取 user-service 的多个实例,并根据策略选择一台健康的节点发送请求。同时支持重试机制,大大提升了稳定性。
第三阶段:统一配置中心 - Config Server
服务多了之后,每个服务都有自己的 application.yml,测试环境、生产环境的数据库连接池、日志级别、第三方API密钥等等都不一样,维护起来非常麻烦。
我们随后引入了 Spring Cloud Config,把所有的配置文件集中放在 Git 仓库中,格式如下:
config-repo/
├── dev/
│ ├── user-service.yml
│ └── product-service.yml
├── prod/
│ ├── user-service.yml
│ └── order-service.yml
各个服务通过 bootstrap.yml 指定要拉取哪个环境的配置:
spring:
cloud:
config:
uri: http://config-server:8080
name: user-service
profile: dev
这样一来,更新某个服务的配置只需要提交 Git 并触发刷新(通过 /actuator/refresh 接口),不再需要重新打包上线,运维同学也轻松了不少。
第四阶段:链路追踪 - Sleuth & Zipkin
前面提到,微服务环境下一次请求可能横跨多个服务,出现错误时很难快速定位根源。我们曾经遇到过这样一个问题:用户支付成功后订单状态迟迟不更新。
当时查看日志发现 Order Service 没有问题,Product Service 也没有异常,但就是没更新状态。最后发现是在 Payment Service 中调用了一个第三方回调通知接口,对方返回了超时,但没有做补偿处理。整个流程缺乏跟踪机制,排查过程花了好几个小时。
为了防止类似情况再次发生,我们引入了 Sleuth 和 Zipkin。
Sleuth 会在每次请求中生成唯一的 trace ID,并在服务间传递,Zipkin 则收集所有服务的 Span 日志并展示完整的调用链路:
比如一次下单操作,调用了 Order -> Inventory -> Payment,最终可以看到每个步骤的耗时、是否有异常:
Trace ID: 4d1e57c2-3a6b-49f5-b6a7-e0f8e5d2c1f3
└─ POST /order/create (Order Service) [1s]
├─ GET /inventory/check (Inventory Service) [200ms]
└─ POST /payment/process (Payment Service) [700ms]
有了这个工具之后,我们日常排查效率提高了至少50%。
第五阶段:服务熔断 - Hystrix
有一次大促期间,用户服务突然因为数据库压力过大导致响应变慢,进而影响到了订单服务无法获取用户信息,整个下单流程都被卡住,用户体验极差。
这个问题暴露了我们的服务调用链中缺乏容错能力。我们后来引入了 Hystrix 来设置熔断降级机制:
@HystrixCommand(fallbackMethod = "defaultUserInfo")
public UserInfo getUserInfo(Long userId) {
return restTemplate.getForObject("/user/info/" + userId, UserInfo.class);
}
public UserInfo defaultUserInfo(Long userId) {
return new UserInfo(userId, "未知用户", "***");
}
当服务不可用或超时时,自动切换为默认值或者缓存数据,虽然部分功能受损,但不至于整体瘫痪。
不过值得一提的是,Netflix 已经停止对 Hystrix 的维护,我们目前也在评估迁移到 Resilience4j 的计划。
效果总结


经过几个月的持续优化和迭代,整个系统的稳定性和服务可维护性明显提升:
- 单个服务的部署时间从原来的几十分钟缩短到几分钟
- 接口响应速度从之前的几百毫秒下降到平均 100ms 内
- 新功能模块开发变得更加轻便,服务边界清晰明确
- 错误定位更快,运维干预更少
尤其是监控和日志方面,借助 Prometheus + Grafana + Zipkin 等工具,我们终于实现了对服务运行状态的实时掌控。
经验分享

如果你正准备或已经开始微服务之旅,结合我的亲身经历,这里有几个建议希望能帮到你:
1. 微服务不是银弹
很多同学一上来就想拆微服务,但其实微服务适合中大型项目或者长期发展的系统。小项目还是建议先用模块化设计+良好的接口隔离,等到真正出现瓶颈再考虑拆分。
2. 数据库设计要慎重
服务拆分时最容易忽视的就是数据库的划分。我们早期没有做好,导致两个服务共用一张表,最后不得不迁移数据、修改接口,浪费了很多精力。
原则:一个服务对应一个数据库,必要时可通过异步同步、消息队列来保证数据最终一致。
3. 接口设计要有前瞻性
服务间的通信不是本地方法调用,要考虑性能和版本兼容性。我们在订单服务和库存服务对接时,一开始设计得太随意,后面改字段、加字段都要两边同时发布,十分痛苦。
建议提前约定好:
- 使用通用数据格式(如 JSON)
- 版本号机制(如 v1/order/create)
- 接口文档及时更新(推荐 Swagger UI)
4. 生产环境的坑比想象得多
- 网络不稳定:服务A调用服务B失败不一定是因为代码问题,可能是网络波动或DNS解析出错。
- 服务漂移:Kubernetes下Pod重启后IP会变,必须依赖注册中心来寻址。
- 日志聚合:别再一个个登录机器看日志,ELK 是必备技能。
- 自动化部署:CI/CD 必须配上,否则手动打 jar 包、上传服务器会让你怀疑人生。
5. 技术选型要有取舍
Spring Cloud 提供了非常丰富的组件,比如 Zuul 改成了 Gateway,Config Server 可能被 Nacos 替代,Hystrix 逐渐被抛弃……
我的建议是:
- 对于中小团队来说,稳比新更重要,不要追求最新最炫的技术
- 优先解决当前痛点,逐步演进,而不是一开始就堆料
- 多关注社区活跃度和官方支持力度
写在最后
微服务这条路不好走,但我始终坚信一句话:“好的系统不是设计出来的,是一点一点折腾出来的。”
如果你刚开始入门 Spring Cloud,不妨从一个简单的 demo 开始,比如写一个“天气预报查询服务”,让它调用另一个“城市信息服务”,加上注册中心、网关、配置中心……你会发现越用越顺,越学越有意思。
希望这篇来自实战的经验分享对你有所启发。如果有任何问题,欢迎留言交流。我也会在后续的文章中继续分享我们在 Spring Cloud + Kubernetes 上的进一步实践,敬请期待!

评论 0