Spring Cloud从零开始:一个真实微服务项目的踩坑实录

Commit写错了
2025-06-25 18:20
阅读 489

引子:为什么我会决定用Spring Cloud做这个项目?

引子:为什么我会决定用Spring Cloud做这个项目?

去年我所在的公司接到一个新项目,是要重构一个老的电商平台后台系统。原来的架构是一套单体应用,代码量已经超过百万行,部署一次需要半小时,出个bug排查要两三天,扩展性更别提了——新增一个功能动辄牵一发而动全身。

我们几个核心开发坐下来讨论技术选型的时候,一致认为这次必须来点不一样的。当时刚好我在上一个项目里接触过Spring Cloud的一些组件,虽然只是浅尝辄止,但那种“拆分+统一治理”的思路让我印象深刻。于是我就提议:“要不这次咱们整个搞一套微服务架构试试?用Spring Cloud。”

结果可想而知,项目组长看着我说:“你确定你搞得定?这可不是小打小闹。”我当时心里其实也没底,但还是拍胸脯说:“没问题,我带着团队一步步来。”

现在回想起来,这个决定确实有点冒险,但也正是这段经历让我真正理解了Spring Cloud的魅力和挑战。


项目背景与初期架构设计

项目背景与初期架构设计

背景简述

我们要重构的是一个电商后台管理系统,包含商品管理、订单处理、用户管理、库存控制等模块。原系统采用的是单体架构,所有模块耦合严重,导致后续维护困难,性能瓶颈突出,特别是高峰期经常出现接口超时甚至挂掉的情况。

技术选型与目标

我们最终决定采用Spring Boot + Spring Cloud的组合来搭建新的微服务架构,具体的技术栈包括:

  • Spring Boot 2.7.x
  • Spring Cloud 2021.0.x(对应Alibaba Nacos 2.0+)
  • 数据库:MySQL 8.0(每个服务独立数据库)、Redis
  • 消息队列:RabbitMQ
  • 配置中心和服务注册发现:Nacos
  • 网关:Gateway + OpenFeign + LoadBalancer
  • 分布式事务:Seata(不过后来没怎么用到)

目标非常明确:解耦、可扩展、易维护、高性能


初遇挑战:服务拆分不是你想拆就能拆

刚开始拆分服务的时候,我们信心满满地按照业务模块划分出了以下几个服务:

系统架构设计图-2

  • product-service(商品服务)
  • order-service(订单服务)
  • user-service(用户服务)
  • stock-service(库存服务)
  • gateway(网关服务)
  • config-server(配置中心)
  • nacos-server(服务注册中心)

听起来是不是挺完美的?但我们很快就在实际开发中遇到了第一个大坑。

问题1:服务边界模糊,调用关系混乱

一开始我们是按“模块”来拆的,比如订单服务负责所有订单相关的逻辑。但随着需求深入,发现有些数据是跨服务的,比如:

用户下单时,需要检查该用户是否符合优惠条件(用户等级)、商品库存是否足够、优惠券是否可用……

这些信息分散在各个服务中,导致我们频繁通过Rest API或Feign进行跨服务调用。一开始觉得“嗯,微服务嘛,这就是正常情况”,但实际情况是:

  • 接口调用链越来越长,响应时间暴涨
  • 服务之间依赖关系复杂,一个小改动可能影响多个服务
  • 出现故障时定位特别难,哪个环节卡住了都不知道

我们当时的调用链大概像这样:

order-service -> user-service (查等级)
             |
             |-> product-service (查价格)
             |
             |-> coupon-service (查可用券)
             |
             |-> stock-service (扣库存)

这样的设计在并发高的情况下,基本就是一个灾难。

问题2:服务注册与配置中心的落地难题

我们选用了Nacos作为服务注册中心和配置中心,但在本地和测试环境一切正常,一旦上生产,各种奇怪的问题就出现了:

  • 注册失败、服务找不到
  • 心跳检测不及时,服务已经下线但还在被调用
  • 配置更新后生效延迟

这些问题一度让我们怀疑“是不是不该用Spring Cloud”……


解决方案:重新定义服务边界 + 合理使用熔断降级

面对上述问题,我们停下来进行了几轮内部复盘和技术评审,最终达成以下共识:

方案一:重新定义服务边界,避免过度拆分

我们意识到最初的拆分方式过于理想化,没有考虑到实际业务场景中的聚合查询事务一致性

于是我们做了调整:

  1. 将某些高频交互的服务合并成一个聚合服务
    • 比如把订单和库存合并为order-stock-service,因为下单和扣库存必须强一致性
  2. 引入异步处理机制
    • 对于非即时性的操作,如积分记录、日志写入,改为通过消息队列异步处理
  3. 增加聚合层API Gateway层
    • 在网关层面做一些聚合调用,减少服务间直接交互次数

这样做之后,系统的调用链条大大缩短,服务间的耦合也明显降低。

方案二:合理使用Hystrix做熔断降级

我们在项目初期完全没考虑熔断机制,直到一次上线过程中,某个下游服务突然宕机,导致整个系统大面积雪崩。

后来我们给所有关键的Feign调用加上了熔断机制,使用的是Netflix Hystrix:

@FeignClient(name = "user-service", fallbackFactory = UserServiceFallbackFactory.class)
public interface UserServiceClient {
    @GetMapping("/users/{userId}")
    User getUserById(@PathVariable Long userId);
}

然后定义回退类:

@Component
public class UserServiceFallbackFactory implements FallbackFactory<UserServiceClient> {
    @Override
    public UserServiceClient create(Throwable cause) {
        return new UserServiceClient() {
            @Override
            public User getUserById(Long userId) {
                // 返回缓存值或者默认值
                return new User(userId, "default_user");
            }
        };
    }
}

虽然Hystrix已经停更,但在当时对我们来说非常实用,至少解决了服务崩溃不影响主线业务的问题。

方案三:优化Nacos的配置与使用方式

Nacos是我们最初踩坑最多的组件之一。下面是几个关键优化点:

  1. 集群部署Nacos Server

    • 我们一开始只用了单节点,结果上线后注册服务失败频率极高
    • 后期改为3节点集群部署,配合MySQL存储元数据,稳定性大大提升
  2. 精细化配置文件管理

    • 不再使用全局刷新配置,而是通过@RefreshScope按需刷新
    • 配置按环境隔离(dev/test/prod)
  3. 健康检查策略调整

    • 增加心跳检测频率和失败重试次数
    • 设置合理的超时时间,防止误判

核心代码示例:服务注册与调用流程

负载均衡配置-1

下面是一个最基础的服务注册和调用的例子:

步骤1:添加依赖(以product-service为例)

pom.xml

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

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

步骤2:启动类加上注解

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

步骤3:配置Nacos地址

application.yml

server:
  port: 8081

spring:
  application:
    name: product-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

步骤4:Feign调用其他服务

@FeignClient(name = "stock-service")
public interface StockServiceClient {
    @GetMapping("/stocks/{productId}")
    Integer getStockByProductId(@PathVariable Long productId);
}

有了这套机制,我们就可以在不同的微服务之间安全、高效地通信了。


踩过的那些坑,我都整理成了经验清单

以下是我亲身经历过的一些典型问题及解决方案:

1. Feign调用无法负载均衡

现象: Feign调用总是指向同一个实例。

原因: 没有正确引入LoadBalancer依赖。

解决办法:

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

提示:Spring Boot 2.6以后官方推荐使用LoadBalancer代替Ribbon了。

2. Nacos客户端连接失败

现象: 微服务启动时报错Connection refused

排查思路:

  • 查看nacos服务是否运行正常(端口监听、进程状态)
  • 检查防火墙是否放行访问
  • 日志里查看是否有网络异常

建议: 上线前务必做网络连通性测试。

3. 多配置文件加载冲突

现象: 生产环境误加载了测试配置。

解决办法:

  • 使用bootstrap.yml专门用于加载配置中心
  • 明确指定namespace区分不同环境
  • 配置文件名最好带上data-id,避免混淆
spring:
  cloud:
    nacos:
      config:
        server-addr: 192.168.1.100:8848
        extension-configs:
          - data-id: product-service-prod.yaml
            group: DEFAULT_GROUP
            refresh: true

结果回顾:微服务架构带来的收益

经过三个多月的开发与磨合,我们的新系统终于顺利上线。效果如下:

  • 部署效率提升明显,每个服务可以单独发布,不再互相影响
  • 系统整体吞吐量提高了近40%,尤其在高并发场景下表现稳定
  • 服务模块清晰,新人上手更快,问题定位更容易
  • 后续新增功能只需拓展新服务,无需改动已有模块

更重要的是,我们团队在整个过程中积累了宝贵的经验,对微服务的理解也更加深刻。


给读者的一些建议

如果你也打算用Spring Cloud开始你的第一个微服务项目,我给你几个忠告:

1. 不要一开始就追求完美拆分

  • 初期可以先粗粒度拆分,随着业务发展逐步细化
  • 服务之间的边界比技术选型更重要

2. 合理选择组件版本

  • Spring Cloud、Spring Boot和Alibaba生态之间版本匹配非常重要
  • 可以参考Spring官网的Compatibility Matrix文档

3. 小步快跑,持续迭代

  • 先搭建起基础结构,再逐步加入熔断、限流、分布式事务等功能
  • 不要试图一口气把所有的中间件都引入进来

4. 监控、日志、链路追踪必不可少

  • 强烈建议接入SkyWalking或Zipkin做链路跟踪
  • 搭配ELK做日志收集与分析
  • Prometheus + Grafana做监控告警

写在最后:微服务从来不是银弹

写到这里,我想说的是,Spring Cloud和微服务并不是万能的

它确实带来了灵活性和可扩展性,但也带来了运维成本、服务治理难度、调试复杂性等一系列问题。很多时候,我们需要在“灵活”和“可控”之间找到平衡。

我依然记得那个深夜,我们几个程序员坐在办公室,看着Prometheus监控图上的绿色指标慢慢稳定下来的那一刻,心里踏实极了。那种成就感,只有真正经历过的人才懂。

希望这篇文章能帮助你在微服务的路上少走些弯路。如果你有任何问题,欢迎留言交流,一起成长。


📌 文章作者:一位曾在电商行业摸爬滚打的Java全栈开发者
💡 技术方向:微服务、Spring Cloud、高并发架构设计
📬 欢迎关注我的技术公众号【TechTalker】,分享更多实战经验和架构思考。

评论 0

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