从零开始搭建微服务:Spring Cloud实战全记录
引言:为什么选择Spring Cloud?
还记得第一次接触微服务架构是在我工作的第二年,那会儿公司业务规模逐渐扩大,传统的单体应用越来越难以支撑快速迭代和高并发的需求。我们开始尝试拆分模块,但面对各个模块之间的通信、配置管理、服务注册与发现等问题,一度陷入困境。
那时候,我和团队翻遍了各种资料,试过Dubbo,也研究过gRPC,但最终还是选择了Spring Cloud——因为它成熟、生态完善,而且上手门槛相对较低,特别适合从传统项目向分布式系统过渡的场景。
今天这篇文章,我想结合自己过去几年在实际项目中使用Spring Cloud的经验,带你一步步从零开始搭建一个完整的微服务架构,并分享我在实战中踩过的坑和收获的心得。
问题描述:我们在做什么项目?遇到了哪些挑战?
2022年初,我们接到了一个新项目:为一家大型连锁超市设计并开发一套全新的线上商城系统,涵盖商品管理、订单处理、库存控制、会员体系等多个子系统。
最开始我们用的是Spring Boot单体架构,一切看起来都很顺利。但随着功能越来越多,代码越来越臃肿,每次发布都提心吊胆,数据库压力陡增,接口响应时间越来越慢。
更糟糕的是,当某一模块出现故障(比如库存服务超时)时,整个系统都有可能被“连带影响”,这在生产环境是绝对不允许的。
主要痛点总结如下:
- 部署困难:所有功能打包在一个jar里,改动小功能也要全量更新。
- 性能瓶颈明显:数据库和某些核心接口成为瓶颈。
- 维护成本高:多人协作时,版本冲突频发,上线风险大。
- 可扩展性差:新增功能只能加模块,无法做到模块级别的横向扩展。
这时候,我们意识到,必须进行服务拆分,走微服务化路线。
解决方案:选型与整体架构设计
我们决定采用Spring Cloud作为微服务框架,基于Netflix开源的一整套组件来构建系统。初期规划的服务包括:
- 用户中心(UAA)
- 商品中心(PMS)
- 订单中心(OMS)
- 库存中心(IMS)
- 支付中心(PAY)
- 网关服务(API Gateway)
- 配置中心(Config Server)
- 注册中心(Eureka Server)
整体结构如下:
[外部请求] --> [Zuul网关] --> [各微服务]
|
[Eureka服务注册中心]
|
[Config配置中心]
这样做的好处:
- 各个服务之间通过Rest或Feign通信
- 所有服务注册到Eureka,实现服务发现
- 使用Zuul作为统一入口,实现路由、权限校验等功能
- 配置文件集中管理,便于维护
- 模块独立部署,互不影响,易于横向扩展
代码实践:从零搭建Spring Cloud项目
第一步:初始化Eureka注册中心
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
application.yml 配置如下:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动后访问:http://localhost:8761 即可看到服务注册页面。
第二步:创建一个简单的微服务(以商品服务为例)
添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类加上注解:
@SpringBootApplication
@EnableDiscoveryClient
public class PmsServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PmsServiceApplication.class, args);
}
}
配置 application.yml:
spring:
application:
name: pms-service
server:
port: 8081
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
启动之后,就可以在Eureka页面看到这个服务已经成功注册。
第三步:服务间调用(使用Feign)
例如在订单中心调用商品中心查询商品信息:
Feign客户端定义:
@FeignClient(name = "pms-service")
public interface ProductFeignClient {
@GetMapping("/products/{id}")
ResponseEntity<Product> getProductById(@PathVariable("id") Long id);
}
在OrderService中注入这个客户端即可完成远程调用。
第四步:引入Zuul网关做统一入口
Zuul负责:
- 请求转发
- 路由配置
- 权限校验
- 日志追踪
启用Zuul只需要添加以下依赖和注解:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
启动类:
@EnableZuulProxy
@SpringBootApplication
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class, args);
}
}
配置:
zuul:
routes:
pms:
path: /pms/**
serviceId: pms-service
oms:
path: /oms/**
serviceId: oms-service
访问 http://localhost:8080/pms/products/123 即可自动转发到对应服务。
踩坑经验:那些年我们掉过的“陷阱”
坑1:Feign Client调用超时
现象: 有时候Feign调用会报错,提示ReadTimeoutException,特别是在负载较高时。
原因分析: 默认情况下Feign没有设置连接和读取超时时间,容易导致线程阻塞。
解决方案:
在application.yml中添加如下配置:
feign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 10000
或者通过自定义配置类实现:
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(5000, 10000);
}
}
坑2:多个服务同时修改数据库导致数据不一致
这个问题出现在订单和库存服务的联动操作中。下单时要扣除库存,如果失败需要回滚。
解决思路: 我们最初尝试用本地事务+手动补偿的方式,但复杂且容易出错。
后来改为借助Saga模式,引入异步消息队列,保证最终一致性。
虽然Spring Cloud Sleuth + Zipkin 可以追踪链路,但在事务性问题上,仍然建议:
- 小范围使用本地事务
- 多服务协作使用事件驱动模型
- 关键业务逻辑配合人工对账机制
坑3:生产环境数据库连接池爆满
上线初期,某次活动期间突然发现大量接口超时。
排查日志后发现是Hikari连接池被占满,SQL执行缓慢。
根本原因:
- SQL未优化
- 数据库索引缺失
- 连接池大小配置不合理(默认只有10)
解决方案:
- 添加慢SQL监控(Prometheus + Grafana)
- 对关键表加索引
- 修改连接池配置:
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
idle-timeout: 30000
max-lifetime: 1800000
效果总结:拆分后的收益
经过近3个月的改造,我们将原来的单体应用拆分为6个微服务,并逐步迁移到Kubernetes集群中运行。
最终效果如下:
| 项目 | 改造前 | 改造后 |
|---|---|---|
| 平均接口响应时间 | 800ms | 250ms |
| 发布频率 | 1次/月 | 1次/周 |
| 故障隔离能力 | 几乎无隔离 | 单服务故障不影响整体 |
| 横向扩展支持 | 不支持 | 支持 |
| 团队协作效率 | 冲突频繁 | 明显提升 |
更重要的是,系统的可维护性大大增强,运维同学也能轻松地通过Prometheus监控各个服务的状态。
经验分享:写给还在路上的你
如果你刚接触微服务,我的建议是:
✅ 从小项目入手,别一上来就想拆分成十几二十个服务
微服务不是银弹,它带来的复杂度远比你想像的多。先从两三个服务练手,逐步深入。
✅ 注重基础设施建设:监控、日志、链路追踪不能少
推荐组合:Spring Cloud Sleuth + Zipkin(链路追踪)、Prometheus + Grafana(监控)、ELK(日志收集)
✅ 合理划分服务边界,避免过度拆分
根据业务领域拆分,而不是技术组件。比如“用户”、“订单”可以单独作为一个服务,而不是把DAO、Service层分别拆出去。
✅ 接口设计尽量保持幂等性,防止重复请求导致状态混乱
尤其是在分布式环境下,网络不稳定可能导致请求重试,这时幂等机制非常重要。
✅ 安全永远是第一考虑:认证授权机制必须做好
我们使用了Spring Security + OAuth2做统一鉴权,并在网关中做了全局拦截。
总结
Spring Cloud是一把利器,但也是一把“双刃剑”。合理使用它可以极大提升系统灵活性和可维护性,但如果滥用或者理解不深,反而会带来很多麻烦。
这篇文章讲了很多细节,是我这几年一路踩坑走来的经验沉淀。希望它能帮你少走弯路,早点走上稳定、可扩展的微服务之路。
如果你有任何疑问,或者想一起讨论某个具体的技术点,欢迎留言或私信交流。微服务的世界很精彩,我们一起探索吧!

评论 0