从“单体地狱”到微服务的重生:Spring Cloud实战手记

深巷里的服务器
2025-06-14 15:32
阅读 4329

引子:被业务压垮的后台系统

还记得去年年底接手那个“巨无霸”项目的场景吗?那是一个原本只有几十个接口的小型电商订单系统,随着业务增长不断膨胀,最终成了一个横跨20多个模块、代码库超过5万行的“怪兽”。我们团队每次上线都像拆定时炸弹,改一个小功能动辄牵一发动全身。更糟糕的是,性能开始捉襟见肘,高峰期响应时间经常突破3秒,用户投诉接踵而来。

老板一句话:“这系统是得重构了。”
我点了点头,心里却一点底没有。虽然平时也看过不少微服务的文章,但真要自己动手从零搭起来,还是头一回。

于是,我开始了与 Spring Cloud 的亲密接触……


挑战来袭:单体系统的致命痛点

数据流转过程-1

在正式启动重构之前,我和团队梳理了几个必须解决的问题:

  1. 代码耦合严重
    订单、支付、库存、物流这些核心模块全部混在一起,改个字段可能影响十几个类。

  2. 部署效率低下
    部署一次要重启整个应用,哪怕只是更新了一个接口,也要停服几分钟。

  3. 性能瓶颈凸显
    高并发下数据库连接池频繁打满,某些慢查询会导致整个应用卡死。

  4. 团队协作困难
    多人同时开发同一个工程时 Git 合并冲突频发,严重影响开发效率。

  5. 版本控制混乱
    功能上线节奏不一致,线上环境存在多个版本共存的问题,排查 bug 成了噩梦。

这个时候,微服务架构就自然进入了我们的视线。


架构选型:为什么选择 Spring Cloud?

我们对比过 Spring Cloud、Dubbo、Kubernetes + Go 等多种方案:

  • Dubbo 虽然轻量,但生态和组件不如 Spring Cloud 完善;
  • Kubernetes + Go 固然先进,但学习曲线太高,团队短期内难以掌控;
  • 最终决定采用 Spring Boot + Spring Cloud 做为基础技术栈。

为什么呢?有几个理由很关键:

  • 团队已有 Java 技术积累;
  • Spring Boot 上手简单,开发效率高;
  • Spring Cloud 组件齐全(注册中心、网关、配置中心等),生态完善;
  • 有 Netflix 和国内社区的成功案例可以借鉴;
  • 可以与现有单体项目逐步拆分,避免“一刀切”的风险。

于是,我们定下了目标:用 Spring Cloud 搭建基础框架,逐步将单体项目拆解成多个独立服务。


实战搭建:Spring Cloud 微服务骨架

第一步:服务注册发现 —— Eureka 的选择与使用

最开始我们用了 Eureka 做服务注册中心。虽然现在 Spring Cloud 已转向 Consul 或 Nacos,但在当时 Eureka 是官方推荐的标准方案。

启动一个 Eureka Server 十分简单:

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

然后给每个服务加上 client:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

服务启动后,就可以在 /actuator/health 查看状态,在 /eureka/apps 查看已注册服务列表。

不过在生产环境遇到一个问题:网络抖动导致服务频繁上下线。为此我们调整了心跳间隔、续约超时时间等参数:

eureka:
  instance:
    lease-renewal-interval-in-seconds: 10
    lease-expiration-duration-in-seconds: 30

小插曲:刚开始没调这两个值,测试环境跑着跑着所有服务突然全挂了,一度以为是代码问题,查了一晚上才发现是网络波动触发了默认机制。那次教训让我们深刻认识到“配置即运维”的重要性。


第二步:服务通信 —— Feign + Ribbon VS OpenFeign

我们选择了 OpenFeign 作为远程调用方案,结合 LoadBalancer 做负载均衡。

定义一个远程调用非常简单:

@FeignClient(name = "order-service")
public interface OrderServiceClient {
    @GetMapping("/orders/{id}")
    OrderDTO getOrderById(@PathVariable("id") Long id);
}

需要注意的一点是:在多环境部署中,Feign 默认使用服务名做负载均衡,但如果想支持 IP 直连调试,我们需要自定义负载策略或者启用 feign.client.config.

还有一个坑就是:如果不加 @LoadBalanced 注解的 RestTemplate 会报错。


第三步:统一 API 网关 —— Gateway 还是 Zuul?

我们最初尝试了 Zuul,但后来切换到了 Gateway —— 因为性能优势明显,且非阻塞式设计更适合高并发场景。

Gateway 的配置很简单:

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

通过这个配置,访问 /api/order/create 的请求会被转发到 order-service 的 /create 接口。

我们还实现了权限校验的全局过滤器,处理 JWT Token 解析、鉴权逻辑:

@Bean
public GlobalFilter authFilter() {
    return (exchange, chain) -> {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        // 校验 token 逻辑...
        if (validToken) {
            return chain.filter(exchange);
        } else {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    };
}

第四步:统一配置管理 —— Config Server + Refresh

Config Server 提供了集中式的配置管理能力,我们可以将不同环境的配置文件放在 Git 仓库里维护:

spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/mycompany/configs.git

各服务只需要指定自己的 name 和 profile:

spring:
  application:
    name: order-service
  profiles:
    active: dev
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config-server

当配置变更时,执行 /actuator/refresh 接口即可热加载。

但有一次我们遇到配置刷新失败的问题,最后发现是因为没有引入 spring-cloud-config-monitor,导致 Webhook 无法触发自动刷新。这个问题提醒我们要时刻关注组件之间的依赖关系。


第五步:链路追踪 —— Sleuth + Zipkin

系统复杂之后,接口调用链变得越来越深。我们引入了 Sleuth 来生成 Trace ID,并上报到 Zipkin:

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

Zipkin 的 UI 界面非常直观,能清晰地看到各个服务之间的调用耗时:

zipkin-example

一次我们排查一个超时接口时,发现某个第三方服务调用平均耗时高达 1.2 秒,而本地日志只显示 300ms,原来是网络传输占用了大部分时间。有了 Zipkin,这种问题一眼就能定位。


数据库设计:如何划分边界?

拆服务的过程中最难的其实不是技术,而是数据边界的划分。举个例子:

原来的订单表包含了商品信息、会员信息、物流信息……如果硬拆,关联查询就成了难题。

我们采用了以下策略:

  • 核心数据留在主服务内(如订单主表);
  • 非强一致性字段允许冗余(如用户昵称、地址);
  • 通过异步消息同步数据(比如下单完成后发布事件到 Kafka);
  • 使用 CQRS 模式分离读写模型(特别是报表类需求);

实际过程中,我们借助了 Kafka 做数据解耦,确保即使下游服务宕机也能保证数据最终一致性。


接口设计:别再裸奔式传参了!

微服务之间调用接口,一开始大家都随便传个 map、json,结果出了问题谁也不知道到底传的是啥。后来我们强制要求:

  1. 所有接口必须声明 DTO 类,不能直接透传实体;
  2. 所有字段需标明含义,必填项明确;
  3. 异常必须封装为标准结构返回;
  4. 加入 Swagger 文档规范;
  5. 对外暴露的服务需要加密签名验证(防止刷单);

例如一个标准的返回结构:

{
  "code": 0,
  "msg": "success",
  "data": { ... }
}

其中 code 需要统一定义:

public enum ResponseCode {
    SUCCESS(0, "成功"),
    PARAM_ERROR(1001, "参数错误"),
    AUTH_FAIL(1002, "权限不足"),
    ...
}

生产运维经验:那些踩过的坑

经过三个季度的磨合,整个系统终于稳定运行,我们也积累了不少运维经验:

  1. 不要忽视 JVM 调优:不同服务的堆内存设置不一样,特别是大数据处理类服务,需要合理分配 Xmx/Xms;
  2. Prometheus + Grafana 是好搭档:监控各服务的 QPS、CPU、GC、TP99、异常率等指标非常有用;
  3. 健康检查接口一定要实现:Kubernetes 或健康检测组件会频繁调用 /actuator/health,不能偷懒;
  4. 灰度发布是个好习惯:我们通过 Gateway 的路由规则,实现按 Header 分流测试新版本;
  5. 日志聚合必不可少:ELK 不仅用来查错,更是性能分析的重要手段;
  6. 限流熔断不能少:Hystrix 现在已经被弃用了,我们换成了 Resilience4j,配合 RateLimiter 实现限流;
  7. 自动化部署工具要用起来:Jenkins + Docker Compose + Ansible,让上线不再靠手动操作。

有一次我们在灰度环境下发现一个 SQL 查询没加索引,导致数据库 CPU 居高不下。幸好 Prometheus 抓住了这一瞬,否则就会上线后炸锅。


效果与收益:重构后的转变

重构完成后,系统的稳定性提升了不止一个档次:

  • 单服务故障隔离能力增强,不会相互拖累;
  • 各个服务独立部署,上线不再需要整站停服;
  • 接口响应时间降低至 200ms 内(提升 5x+);
  • 日均支撑 200W+ 请求毫无压力;
  • 新同学上手更快,只需关注单一职责的服务;
  • 支撑了后续营销活动的爆发流量,扛住了大促高峰;
  • 架构清晰,为未来扩展埋下伏笔(比如接入 AI 推荐服务)。

更重要的是——大家的工作节奏变了。从前每天都在救火,现在可以有更多精力做优化、搞创新。


经验分享:如果你也在做微服务

如果你也在走这条路,我想送你几点建议:

  1. 不要为了微服务而微服务,先评估是否真的需要;
  2. 服务边界比技术选型更重要,设计阶段请慎重;
  3. 基础设施先行:注册中心、配置中心、监控体系优先建设;
  4. 保持服务小粒度但合理,一个服务最好只做一件事;
  5. API 规范要严格遵守,不然迟早变成“分布式单体”;
  6. 持续集成不可省,自动化构建+部署才是王道;
  7. 文档不能少,Swagger、Postman、Confluence 都要有;
  8. 保留“一键回滚”能力,出问题时这是救命稻草。

微服务不是银弹,但它确实能帮助我们更好地应对复杂的业务挑战。就像我亲历的这次重构,从一团乱麻到结构清晰的过程,不仅改变了系统,也改变了我们的思维方式。


结语:从零出发,走向更大的舞台

回首这一年,从对 Spring Cloud 的陌生,到现在能在凌晨三点接到告警时迅速定位问题,这段旅程充满挑战但也无比充实。

微服务不是终点,它只是通往更大规模系统的起点。我相信只要你肯动手去试,愿意花时间打磨每一个细节,Spring Cloud 一定会带给你惊喜。

愿你也在微服务的世界里,走出一条属于自己的路。

如有疑问,欢迎留言交流!

评论 0

最热最新
暂无评论
深巷里的服务器Lv.1
0
影响力
0
文章
0
粉丝