Spring Cloud从零开始:微服务的实践之路

表结构守护者
2025-06-16 13:27
阅读 316

开篇:为什么选择Spring Cloud?

开篇:为什么选择Spring Cloud?

我是某家电商平台技术团队的一名负责人。两年前,我们公司正处于快速扩张期,单体架构的系统已经越来越难以支撑不断增长的业务量和并发请求。尤其是大促期间,系统经常出现卡顿、超时甚至崩溃的现象。

那会儿我们的应用是一个将近200万行代码的Java后端,部署在几台Tomcat服务器上,前端用的是Vue,数据库主要是MySQL + Redis。虽然整体结构还算清晰,但模块之间高度耦合,改动一个功能动辄需要测试整个项目,上线时间被拉得很长。

为了打破这种“牵一发而动全身”的局面,我们决定向微服务架构转型。调研之后,最终选用了Spring Cloud Alibaba作为核心技术栈,结合Nacos做服务注册与发现,加上Sentinel做流量控制,Seata处理分布式事务,以及Gateway来做API网关。

这篇文章,就是基于我们团队从零搭建微服务架构的实际经验写成的。我会尽可能多地用实际场景来说明问题、展示解决方案,并分享我们在实践中踩过的坑和总结的经验教训。


问题描述:微服务不是银弹,但也确实解决了关键痛点

问题描述:微服务不是银弹,但也确实解决了关键痛点

微服务并不是适用于所有项目的万能方案,但在我们当时的背景下,它是最佳选择之一。那么具体来说,我们遇到了哪些问题呢?

1. 模块耦合严重,维护成本高

原本的系统里订单、商品、库存等模块都混在一个工程中,每次修改都要全量打包、部署。一个模块出错可能影响其他模块的功能,尤其在频繁迭代的情况下,开发效率极其低下。

2. 高并发支撑能力弱

大促期间QPS峰值能达到上万,但因为是单体架构,扩容只能通过加Tomcat实例,无法做到对某些热点模块单独扩容。

3. 灰度发布与AB测试困难

没有统一的服务治理机制,想做灰度发布、路由规则调整等操作非常麻烦,必须靠手动配置或改代码来实现。

这些问题归根结底都指向了一个核心诉求:解耦、自治、可控


解决方案:Spring Cloud全家桶实战落地

解决方案:Spring Cloud全家桶实战落地

确定了要引入微服务架构后,我们制定了分阶段的实施计划:

  • 第一阶段:完成服务拆分、注册中心搭建;
  • 第二阶段:打通服务通信链路,引入熔断限流;
  • 第三阶段:完善日志监控、持续集成、安全认证等配套设施;
  • 第四阶段:实现自动化部署与弹性伸缩。

接下来我会以我们最典型的三个服务为例,讲述我们的设计思路和关键技术实现细节:

服务拆分:订单服务、商品服务、用户服务

初始拆分策略

我们将原有系统的模块按领域拆分,每个模块作为一个独立的Spring Boot服务,各自拥有独立的数据库(当然有些表是共用的,比如用户信息)。

最初我们遇到的问题是:

怎么定义一个“合理”的服务边界?

举个例子,商品详情页包含价格、库存、评论、推荐信息等多个字段。这些信息来自不同的模块。如果全部调用远程服务,会不会导致接口响应变慢?

我们尝试了几种方式:

  1. 本地缓存+异步更新:使用Redis做商品基本信息的缓存,每个小时从商品服务同步一次数据;
  2. 聚合服务层:新建一个叫product-view的服务,专门负责组合多个子服务的数据;
  3. 跨服务查询优化:允许部分非实时敏感字段走冗余表或Elasticsearch,提高访问性能。

最后我们选择了第二种聚合服务层方案,虽然增加了服务数量,但从长期可维护性来看,是值得的。

数据库设计上的取舍

服务拆分之后,数据库也自然分离。但这也带来了一些问题,例如:

  • 用户订单中的商品信息应该保存快照,避免商品信息变更后订单展示异常;
  • 库存扣减逻辑如何保证一致性?我们初期采用的是定时任务+人工补偿,后来接入了Seata做分布式事务管理。

关于接口设计,我们也做了一些约定:

  • 所有服务对外提供RESTful API,遵循标准返回格式;
  • 增加版本号支持多版本兼容,如 /v1/product/detail;
  • 所有请求必须携带traceId,便于链路追踪。

技术选型与实现

使用Nacos做服务注册与发现

Spring Cloud原生推荐的是Eureka,但我们团队更倾向于国产生态,而且后续要用到Alibaba全家桶,所以直接选择了Nacos。

部署Nacos Server时我们一开始用的是单机模式,结果压测的时候发现注册列表读写压力很大,特别是在服务重启频繁的情况下,会导致集群状态不稳定。后来改成集群模式 + MySQL持久化存储,才解决了这个问题。

配置中心也是一样,我们之前所有的配置都是放在各个项目的application.yml文件中。服务多了以后,配置管理变得极为复杂。

所以我们把通用配置项都放到Nacos配置中心,比如:

spring:
  datasource:
    url: jdbc:mysql://...
    username: ...
    password: ...

服务启动时只需要指定namespacegroup即可加载对应环境的配置。

服务间通信:RestTemplate还是Feign?

早期我们用的是Spring自带的RestTemplate,但随着服务增多,代码冗余明显,而且参数传递、错误处理都需要自己封装。

于是我们切换到了Feign,配合Ribbon实现了客户端负载均衡,并且用OpenFeign做了接口声明式调用,效果很好。

这里有一个真实踩坑案例:有一次某个服务调用一直失败,但日志显示连接正常。排查后才发现,原来Feign默认不启用Hystrix降级(即使你引入了相关依赖),需要手动开启熔断。

解决办法是在配置中添加:

feign:
  hystrix:
    enabled: true

然后为每个Feign Client编写fallback类,实现容错处理。

流量控制 & 安全防护:Sentinel + Gateway

随着服务越来越多,我们意识到流量控制和权限校验的重要性。

我们用了Spring Cloud Gateway作为统一入口,做了如下事情:

  • 请求路由:根据路径将请求转发到对应的下游服务;
  • 权限验证:对接JWT Token,拦截非法请求;
  • 请求限流:使用Sentinel为关键接口设置TPS限制;
  • 黑白名单:支持根据IP做临时封禁。

说到Sentinel,它真的是一个非常好用的组件。我们通过仪表盘可以实时看到接口的QPS、响应时间、线程数等指标,还可以在线调整限流规则。

不过需要注意一点:Sentinel默认只记录最近60秒内的数据,如果你要做更长时间的趋势分析,最好配合Prometheus + Grafana。

分布式事务:Seata的抉择与实践

当订单服务和库存服务需要同时修改数据的时候,事务一致性就成了头号难题。

最初我们用的是“伪事务”——即先扣库存再建订单,失败就回滚库存。这个方法简单但容易丢数据,尤其是在网络波动或超时情况下。

后来我们引入了Seata,采用AT模式进行全局事务协调。整体流程大致如下:

  1. 订单服务发起下单请求;
  2. 底层Seata生成XID,开启全局事务;
  3. 分别调用库存服务、优惠券服务等,执行本地SQL;
  4. 如果其中一个服务失败,Seata自动回滚所有分支事务;
  5. 提交成功则标记事务完成。

使用过程中我们发现几个需要注意的地方:

  • 必须给所有涉及的数据库建立undo_log表;
  • SQL不能使用批量插入(会被Seata识别为不支持);
  • 表主键必须明确,不能使用UUID等不可逆字段;
  • 一定要配合重试机制,避免因网络抖动造成误判。

这套机制在我们的系统中运行稳定,特别是在大促期间发挥了重要作用。


效果总结:微服务带来的改变和收益

微服务架构改造完成后,我们得到了以下几点显著提升:

指标 改造前 改造后
接口响应时间 平均400ms 平均200ms
上线频率 两周一次 每周多次
部署效率 全量部署 按服务灰度发布
异常定位耗时 小时级 分钟级
大促稳定性 多次崩盘 零故障

除了性能提升外,还有一个重要的“软实力”提升:团队协作更加高效

以前一个改动往往牵涉多人配合,现在大家可以专注于各自负责的服务,只要接口文档明确,就能并行开发、快速上线。此外,我们还建立了统一的监控告警体系,大大提升了运维效率。


经验分享:给刚入门同学的建议

如果你也正准备踏上微服务这条路,下面这些经验希望对你有帮助:

1. 服务边界的设计比你想得更重要

不要一开始就把服务拆得太细,那样反而会造成过度设计。建议先从业务领域出发,按DDD的方式划分上下文边界。后期可以根据实际发展情况灵活调整。

小技巧:先搭一个骨架服务,跑通基础链路,再逐步细化。

2. 不要忽视服务治理基础设施

服务注册、配置中心、限流熔断、链路追踪……这些看似辅助的东西,才是微服务稳定运行的关键。否则你会发现:

“我明明把服务拆开了,为什么更难管理了?”

推荐使用:

  • Nacos:集服务注册+配置中心于一体,开箱即用;
  • Sentinel:限流、降级、熔断一站式搞定;
  • SkyWalking / Zipkin:做链路追踪;
  • Prometheus + Grafana:做指标采集和可视化。

3. 微服务不等于无限扩容

很多人以为微服务就可以随意扩容,实际上:

  • 真正能扩容的是无状态的服务;
  • 有状态的服务(如数据库、缓存)扩容仍然受限;
  • 即使是Kubernetes也难以解决网络延迟和IO瓶颈问题。

建议在设计之初就要考虑横向扩展的能力边界

4. 搞清“分布式事务”的本质

分布式事务不是用来解决一切问题的魔法药,它只是权衡之后的折中方案。我们最终也没有完全依赖Seata,而是根据场景分别采用了:

  • 本地消息表
  • TCC补偿事务
  • Saga模式

不同场景选择不同的方案才是王道。

5. 要有“DevOps”意识

服务拆分越多,越考验你的自动化能力。我们目前做到:

  • Jenkins流水线自动构建镜像;
  • GitLab Webhook触发部署脚本;
  • Kubernetes滚动升级 + 健康检查;
  • Prometheus告警 + 钉钉通知;
  • ELK集中日志分析。

这些东西哪怕一开始做不完,也要有一个清晰的演进路线图。


结语:微服务是一条值得走下去的路

微服务从来不是一个简单的技术选型题,而是一个涉及架构、协作、运维、安全等多方考量的系统工程。刚开始的时候我们也会焦虑、迷茫,甚至怀疑是否真的有必要做这么大的重构。

但当我们真正走过这趟旅程之后,回头看:

是的,这条路并不轻松,但它让我们的系统更具生命力,也让团队更有战斗力。

如果你正在考虑是否要迈出这一步,请相信:

不是所有系统都适合微服务,但当你不得不面对快速增长和复杂演化的挑战时,微服务也许就是你手中那个不可或缺的工具。

愿你在探索的路上少走弯路,多些收获。


文章作者:张浩 | 某电商公司后端技术负责人
联系方式zhanghao@example.com
个人博客https://zhblog.tech
欢迎交流技术、架构、团队管理等相关话题

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝