Spring Cloud从零开始:一个后端开发者的实战笔记

孙平_云计算
2025-06-24 01:30
阅读 500

引子:微服务真的能救项目于水火吗?

引子:微服务真的能救项目于水火吗?

那是一年前,我在一家中型电商公司做后端架构设计。彼时我们的系统还只是一个单体应用——Java写的Spring Boot工程,前端Vue加个Nginx代理,后台接口全部写在一个库里。随着用户量增长、功能增加、需求迭代速度加快,这个项目慢慢变成了我们眼中的“噩梦”。

代码越来越庞大,改个功能要跑遍十几个包,线上发布动辄两小时起跳;一旦出错,排查起来整个团队都得蹲守;新来的同事根本看不懂模块之间的调用关系……我们意识到,再不重构迟早要翻车。

于是,我提出了微服务化方案,而技术选型的主旋律就是:Spring Cloud Alibaba + Nacos + Gateway + Seata + Sentinel 这一套主流组合拳。

接下来我想分享一下这次转型的全过程:从一开始的懵懂试水,到中间踩坑无数,再到最终落地稳定运行的一整套流程,希望给同样在走这条路的小伙伴们一点启发和参考。


问题描述:当单体应用撑不住业务扩展

问题描述:当单体应用撑不住业务扩展

我们的核心服务叫 order-service,一开始是直接和其他功能糅合在一起的,比如商品信息、支付处理、库存校验等模块都在同一个工程里。但问题是:

  • 耦合严重:订单创建需要调用商品服务获取价格、调用库存服务减少库存、调用支付服务生成预付单,这些逻辑全挤在一起。
  • 发布风险高:每次上线都是全量部署,小修也得全站灰度测试。
  • 并发支撑能力有限:一到大促就卡顿,GC频繁,响应延迟陡增,甚至出现过雪崩效应导致服务完全瘫痪。

这种情况下,我们决定先对订单中心进行拆分,独立为一个微服务,并逐步带动其他模块的拆解。


解决方案:从零搭建基于Spring Cloud的微服务系统

数据库设计模型-1

解决方案:从零搭建基于Spring Cloud的微服务系统

第一步:确定技术栈和架构目标

我们在调研之后,锁定了以下技术选型(2023年初):

组件 技术 理由
注册与发现 Nacos 支持服务注册、健康检查,且支持配置中心,生态完善
配置中心 Nacos Config 和注册中心一体化管理,动态刷新配置
网关 Spring Cloud Gateway 性能优于Zuul,非阻塞式网关框架
负载均衡 Ribbon 结合OpenFeign使用,调用更轻便
熔断限流 Sentinel 国内社区活跃,控制策略丰富,易于集成
分布式事务 Seata 支持TCC和AT模式,适合电商场景
日志追踪 SkyWalking / Sleuth+Zipkin 后期接入监控平台

架构目标如下:

  1. 模块间解耦,职责明确;
  2. 接口粒度清晰、可拓展;
  3. 可横向扩展,按需扩容;
  4. 支持灰度发布、链路追踪、熔断限流等高级特性。

第二步:搭建基础项目结构

我们选择了Maven多模块项目方式组织代码,整体结构如下:

springcloud-mall/
├── order-service         // 订单服务
├── product-service       // 商品服务
├── user-service          // 用户服务
├── gateway               // API网关
├── common                // 公共依赖,如DTO、异常处理等
└── config-center         // 配置中心客户端配置

所有服务都使用Spring Boot + Spring Cloud 2022.x 版本。

示例:订单服务的启动类

@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

接着,在每个服务的bootstrap.yml中统一拉取Nacos的配置:

spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        extension-configs:
          - data-id: application.yaml
            group: DEFAULT_GROUP
            refresh: true

这一部分很简单,但要注意的是:Nacos配置文件优先级比本地配置高,而且默认不会主动刷新。要开启refresh才生效。


第三步:实现基本的服务间通信

订单服务要调用商品服务,这里我们采用最常用的方式:OpenFeign + LoadBalancer。

先加上依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

然后定义Feign Client接口:

@FeignClient(name = "product-service")
public interface ProductServiceClient {

    @GetMapping("/api/product/{id}")
    ResponseEntity<ProductDTO> getProductById(@PathVariable("id") Long id);

}

这样在Order Service中就可以通过注入该接口来完成跨服务调用了。注意要启用FeignClient,可以在主类上加@EnableFeignClients注解。


第四步:接入网关Gateway统一入口

网关层我们采用Spring Cloud Gateway,好处是它基于Netty,性能优于传统Zuul。

配置路由规则示例:

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1

这样就能把 /api/order/xxx 请求转发到 order-service/xxx 接口上。

同时,我们也做了鉴权过滤器和日志拦截器的封装,这部分后面会专门展开讲。


第五步:服务治理:熔断、限流、降级

这部分我们采用了阿里巴巴的Sentinel,效果非常好。

在Order Service中引入Sentinel starter:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

然后添加如下配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080 # 控制台地址
      filter:
        enabled: true

接着,我们对一些关键接口进行限流配置:

@GetMapping("/create")
@SentinelResource(value = "createOrder", blockHandler = "handleCreateOrderException")
public ResponseEntity<?> createOrder() {
    ...
}

public ResponseEntity<?> handleCreateOrderException(BlockException ex) {
    return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("请求太频繁,请稍后再试");
}

通过Sentinel Dashboard可以实时查看QPS、线程数等指标,并动态设置限流阈值,非常方便。


第六步:分布式事务Seata的接入

因为订单服务会涉及扣减库存、更新支付状态等多个步骤,必须保证数据一致性。我们采用了Seata的AT模式(基于数据库undo_log实现事务回滚)。

客户端配置如下:

seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group

然后在创建订单的方法上加上全局事务注解:

@PostMapping("/create")
@GlobalTransactional(rollbackFor = Exception.class)
public ResponseEntity<?> createOrder() {
    reduceInventory(); // 扣库存
    deductBalance();   // 扣余额
    createOrderRecord();
}

只要其中任意一个方法抛出异常,整个事务就会自动回滚。Seata的自动回滚机制让分布式事务变得轻松许多。

当然,实际生产环境中还是建议结合TCC手动补偿机制一起使用,尤其是对金额类操作,保障性更强。


效果总结:服务稳定性提升显著

服务器部署方案-2

效果总结:服务稳定性提升显著

经过三个月的迁移和优化,我们将原本的单体应用逐步拆分为以下几个服务:

  • 用户中心(User)
  • 商品中心(Product)
  • 库存服务(Stock)
  • 订单中心(Order)
  • 支付中心(Payment)
  • 网关 + 鉴权服务(Auth/Gateway)

上线之后,系统的几个重要指标明显改善:

  • 单服务重启时间从原来平均10分钟缩短至1分钟左右
  • 大促期间系统崩溃次数减少了95%
  • 新功能迭代周期缩短了40%
  • 线上故障定位效率提高60%,得益于SkyWalking的接入
  • 通过Sentinel动态限流,避免了多次流量高峰导致的宕机事故

这让我深刻体会到:微服务不是为了复杂而复杂,而是为了解耦和提效。


经验分享:一路走来踩过的坑

在这趟Spring Cloud转型之路中,我也踩了不少坑,想把这些经验分享给大家:

1. 不要一上来就把系统全拆开

微服务并不是灵丹妙药,也不是解决所有问题的万金油。刚开始一定要先从业务边界清晰的部分做起,像我们就是先从订单中心切出来的,确保每一个服务都有明确定义的对外接口和职责范围。

否则容易陷入“拆成更多单体”的陷阱。

2. 配置中心要尽早接入

很多人会忽略配置管理的重要性。早期我们也是直接写在yml文件里,结果一上生产环境各种乱套:不同环境配置混杂、密钥泄露、版本混乱……

后来统一使用Nacos管理所有服务的application.yaml,包括dev、test、prod三个namespace,大大提升了运维效率。

3. 接口设计要遵循幂等原则和开放封闭原则

这一点特别重要。很多初学者写接口的时候只是简单地提供一个URL,但实际上应该考虑幂等性。

例如订单取消接口,即使被反复调用也应该只执行一次。可以通过唯一ID或Redis记录来防止重复处理。

另外,接口要尽量保持对修改关闭,对扩展开放,未来升级也不会影响老客户端。

4. 别小看日志和监控

我们刚上线初期没接入日志聚合和链路追踪,遇到问题只能靠人工grep日志,效率极其低下。

后来接入了SkyWalking,不仅可以看到服务调用链、慢SQL、JVM内存变化,还能实时看到API接口的TP指标,极大降低了排查成本。

所以一定要提前规划好日志收集、链路追踪和指标监控体系。

5. 做好灰度发布和蓝绿部署准备

现在的服务越来越多,发版风险越来越大。我们采用K8s + Helm Chart管理各个服务,配合Prometheus + Grafana实现滚动更新和自动化监控,有问题可以立即回滚。

现在每天都有多个服务灰度更新,再也不怕“上线等于下岗”了😂


写在最后:持续优化比一蹴而就更重要

Spring Cloud生态很庞大,想要一口吃成胖子是不可能的。真正能帮我们解决问题的,不是那些天花乱坠的概念,而是对技术的深入理解和真实业务场景的打磨

微服务改造是一个过程,而不是一个瞬间的动作。我始终相信一句话:“软件架构没有银弹,只有不断演化。”

如果你现在也在尝试微服务转型,不妨从小处着手,逐步推进。过程中你会遇到各种挑战,但每一次问题的解决,都会成为你宝贵的财富。

希望我的亲身经历和实践经验,对你有所帮助!

如果还有关于具体某一部分你想深入了解,欢迎留言或者私信交流。我会尽力分享我在工作中学到的每一个细节和技术感悟。


Keep coding, stay curious.

评论 0

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