Spring Cloud 入门与实战:从微服务协作到网关与负载均衡
Spring Cloud 入门与实战:从微服务协作到网关与负载均衡
适合读者:了解 Java、Maven 和 Spring Boot,希望系统理解 Spring Cloud,并搭建一条微服务请求链路的开发者。
版本核对日期:2026-10-23。本文示例采用 Java 17、Spring Boot 4.1.0、Spring Cloud 2025.1.2;这是明确的示例基线,不代表你阅读时的最新补丁版本。
一个订单系统刚起步时,用户、商品、库存和支付都放在一个 Spring Boot 应用里,开发方便,部署也简单。
随着业务发展,库存需要独立扩容,支付需要单独发布,不同团队开始负责不同模块。于是,一个应用拆成了多个服务。
拆分之后,新的问题随之而来:
- 订单服务怎么找到库存服务?
- 库存服务有三个实例,请求应该发给谁?
- 下游响应变慢,上游线程会不会被拖住?
- 几十个服务的配置怎样保持一致?
- 一次请求经过多个服务,出问题后从哪里查起?
Spring Boot 让单个应用更容易构建,Spring Cloud 则帮助多个应用更有秩序地协作。
本文不追求把所有组件一次性堆上去,而是先理解它解决什么问题,再搭建一个最小示例,最后讨论真正上线时必须补齐的工程能力。
一、Spring Cloud 到底是什么?
Spring Cloud 不是一个独立运行的服务器,也不是某一个注册中心,而是一组面向分布式系统的项目与集成能力。
它围绕服务发现、负载均衡、网关、集中配置、容错和消息驱动等需求,提供统一抽象和自动配置,减少重复的基础设施接入工作。
可以用下面这张表理解相关技术的分工:
| 技术或架构 | 主要解决的问题 | 不负责替你完成的事情 |
|---|---|---|
| Spring Boot | 构建、配置和运行单个应用 | 自动划分业务边界 |
| Spring Cloud | 应用之间的分布式协作 | 自动保证业务一致性 |
| 微服务架构 | 按业务能力拆分系统并独立演进 | 天然降低系统复杂度 |
| Kubernetes | 容器编排、部署、调度与基础服务发现 | 替代全部应用层业务治理 |
这里有一个常见误区:引入 Spring Cloud,不等于必须拆微服务;拆成微服务,也不等于必须用齐 Spring Cloud。
如果团队规模不大、业务边界还在频繁变化,一个模块划分清晰的单体应用,往往比十几个相互依赖的服务更容易维护。
微服务的价值在于独立演进,不在于服务数量看起来够多。
二、核心能力:先看问题,再选组件
2.1 服务发现:找到服务在哪里
如果订单服务把库存服务地址写死为 http://192.168.1.10:8080,库存服务一迁移,调用方就得修改配置。
服务发现把“服务名称”和“实例地址”分开:
inventory-service
├── 10.0.0.11:8080
├── 10.0.0.12:8080
└── 10.0.0.13:8080
调用方通过服务名获取实例列表,不再直接依赖某一台机器。
Spring Cloud Commons 提供 DiscoveryClient、ReactiveDiscoveryClient 等抽象,具体实现可以对接 Eureka、Consul、ZooKeeper 等基础设施。Nacos 通常通过 Spring Cloud Alibaba 的相关集成接入,需要额外核对 Alibaba、Spring Cloud 和 Spring Boot 的兼容关系。
注册中心的健康检查、实例过期策略和客户端缓存共同决定故障实例多久能从调用链中消失。它不是一个能瞬间感知所有故障的“上帝视角”。
2.2 负载均衡:从实例列表中选一个
服务发现回答“有哪些实例”,负载均衡回答“这次选哪个实例”。
Spring Cloud LoadBalancer 提供客户端负载均衡能力,可以与 Gateway、带负载均衡能力的 HTTP 客户端等配合使用。
默认实现通常采用轮询,也可以按需求定制实例选择策略。
请求 A → inventory-service → 实例 1
请求 B → inventory-service → 实例 2
请求 C → inventory-service → 实例 3
如果部署在 Kubernetes 上,也可以使用 Service 和 DNS 等平台能力。应用层和平台层不是二选一,但要明确每一层负责什么,避免重复治理却没人真正负责。
2.3 网关:统一入口,不是第二个业务系统
Spring Cloud Gateway 负责路由转发,并能通过过滤器接入认证、限流、请求头处理和可观测性等能力。
客户端
│
▼
API Gateway
├── /api/orders/** → 订单服务
├── /api/products/** → 商品服务
└── /api/users/** → 用户服务
Gateway 包含基于 WebFlux 和基于 Web MVC 的不同服务器实现,依赖和配置不能混用。
本文采用 Gateway Server WebFlux。它运行在响应式技术栈上,自定义过滤器中不应直接执行阻塞式数据库访问或同步远程调用,否则容易堵住事件循环线程。
网关可以统一检查身份,但订单是否属于当前账号、某项操作是否被允许,仍然需要由业务服务执行资源级授权。
2.4 其他常见能力
| 需求 | 常见选择 | 关键理解 |
|---|---|---|
| 集中配置 | Spring Cloud Config | 集中管理不等于配置变更会自动、安全地作用于所有对象 |
| 配置变更事件传播 | Spring Cloud Bus | 通常借助消息基础设施传播事件,不负责替业务验证配置 |
| HTTP 调用 | RestClient、WebClient、Spring HTTP Service Clients、OpenFeign | 不同客户端的同步、响应式和声明式模型各有适用场景 |
| 熔断与隔离 | Spring Cloud CircuitBreaker、Resilience4j | 抽象、实现以及具体策略需要分别理解 |
| 消息驱动 | Spring Cloud Stream | 通过 Binder 对接消息中间件,不会自动获得端到端 exactly-once |
| 指标与链路追踪 | Actuator、Micrometer、OpenTelemetry 等 | 涉及 Spring Boot 与更广的生态,不全部属于 Spring Cloud |
这些能力是一张工具清单,不是一张必须全部安装的购物清单。
三、版本管理:不要凭感觉拼依赖
Spring Cloud 使用发布列车,也就是 Release Train,管理多个子项目的兼容组合。
2025.1.2 是发布列车版本,不代表 Gateway、Commons、Config 等组件都使用这个版本号。例如,本文所用的 Spring Cloud 2025.1.2 BOM 管理的 Gateway 版本是 5.0.2。
根据版本核对时的官方项目页,主要对应关系如下:
| Spring Cloud 发布列车 | Spring Boot 版本线 | 说明 |
|---|---|---|
| 2025.1.x | 4.0.x;从 2025.1.2 起也支持 4.1.x | 本文选择 2025.1.2 + 4.1.0 |
| 2025.0.x | 3.5.x | 核对时项目页已标记 EOL |
| 2024.0.x | 3.4.x | 核对时项目页已标记 EOL |
EOL 表示该版本线已结束相应的维护周期,不表示已经部署的应用会突然无法运行;兼容性与支持状态也不是一回事。
实际选型建议遵循下面的顺序:
- 确定 Java 和 Spring Boot 版本线。
- 在官方兼容表中选择匹配的 Spring Cloud 发布列车。
- 在所选版本线中采用适合项目的受维护补丁版本。
- 通过
spring-cloud-dependenciesBOM 统一管理 Cloud 组件版本。 - 再核对数据库驱动、第三方 Starter 和其他生态组件的兼容性。
不要通过关闭兼容性检查来“修复”版本不匹配。检查不报错了,不代表依赖之间就能正常协作。
四、实战:用一个网关访问两个服务实例
这一节只搭建一条清晰的链路:
┌── order-service :8081
客户端 → Gateway :8080 ──┤
└── order-service :8082
为了把注意力集中在路由和负载均衡上,示例使用 SimpleDiscoveryClient,通过配置提供服务实例列表,不额外部署注册中心。
它是静态服务发现,不包含自动注册、心跳或故障摘除。后续替换为注册中心时,需要调整实例发现的来源,而不是把业务路由全部重写。
4.1 环境与目录
准备 Java 17 或更高的兼容版本,以及 Maven 3.6.3 或更高版本,并确认 java 与 mvn 命令可用。
示例目录如下,后面的代码块与文件一一对应:
spring-cloud-demo/
├── pom.xml
├── gateway/
│ ├── pom.xml
│ └── src/main/
│ ├── java/com/example/gateway/GatewayApplication.java
│ └── resources/application.yml
└── order-service/
├── pom.xml
└── src/main/
├── java/com/example/order/OrderServiceApplication.java
├── java/com/example/order/OrderController.java
└── resources/application.yml
4.2 父工程:统一版本与打包插件
根目录 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<modules>
<module>gateway</module>
<module>order-service</module>
</modules>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.1.2</spring-cloud.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
BOM 的职责是管理版本,不是引入依赖。各模块仍然需要声明自己真正使用的 Starter。
4.3 网关模块:依赖与启动类
gateway/pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>
这里使用 WebFlux 网关 Starter,不再混入 spring-boot-starter-webmvc。
gateway/src/main/java/com/example/gateway/GatewayApplication.java:
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
4.4 配置服务列表与路由
gateway/src/main/resources/application.yml:
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
discovery:
client:
simple:
instances:
order-service:
- uri: http://127.0.0.1:8081
- uri: http://127.0.0.1:8082
gateway:
server:
webflux:
routes:
- id: order-route
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
metadata:
connect-timeout: 1000
response-timeout: 3000
这段配置包含五个关键点:
order-service是逻辑服务名,下面列出两个实例。lb://order-service表示通过负载均衡解析目标实例,不是普通的 HTTP URL。Path=/api/orders/**用于匹配外部请求路径。StripPrefix=1去掉第一个路径段,使/api/orders/1001转发为/orders/1001。- 路由级
metadata中的连接和响应超时均以毫秒配置;它们不是整条业务链路的总截止时间。
本文使用 Gateway 5.x 的 spring.cloud.gateway.server.webflux.routes 配置路径。旧文章中的配置前缀和 Starter 名称,不能不看版本就直接搬过来。
4.5 订单模块:提供查询接口
order-service/pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>order-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
</dependencies>
</project>
订单服务只是提供 HTTP 接口,不需要为了“加入微服务架构”而引入一整套 Cloud 依赖。当前示例的实例列表由网关配置提供。
order-service/src/main/java/com/example/order/OrderServiceApplication.java:
package com.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
order-service/src/main/java/com/example/order/OrderController.java:
package com.example.order;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/orders")
public class OrderController {
private final int port;
public OrderController(@Value("${server.port:8081}") int port) {
this.port = port;
}
@GetMapping("/{orderId}")
public Map<String, Object> findOrder(@PathVariable("orderId") long orderId) {
return Map.of(
"orderId", orderId,
"status", "CREATED",
"instance", "order-service:" + port
);
}
}
这个接口返回模拟数据,并用端口标识响应实例,不涉及数据库和真实订单状态。
order-service/src/main/resources/application.yml:
server:
port: 8081
spring:
application:
name: order-service
4.6 打包、启动与访问
在 spring-cloud-demo 根目录执行:
mvn clean package
打包完成后,分别在三个终端中启动进程。三个终端都以项目根目录为当前目录。
终端一,启动第一个订单实例:
java -jar .\order-service\target\order-service-1.0.0.jar --server.port=8081
终端二,启动第二个订单实例:
java -jar .\order-service\target\order-service-1.0.0.jar --server.port=8082
终端三,启动网关:
java -jar .\gateway\target\gateway-1.0.0.jar
等待三个应用完成启动,在另一个 PowerShell 终端连续访问网关:
1..6 | ForEach-Object {
Invoke-RestMethod -Uri "http://localhost:8080/api/orders/1001"
} | Format-Table orderId, status, instance
单次响应的数据结构如下,JSON 字段顺序不影响含义:
{
"orderId": 1001,
"status": "CREATED",
"instance": "order-service:8081"
}
连续请求时,可以观察 instance 在 order-service:8081 和 order-service:8082 之间切换。首次落到哪个实例不应作为测试前提。
至此,我们已经串起了三项能力:
路由匹配 → 根据服务名发现实例 → 负载均衡选择实例并转发
4.7 做一次故障实验
停止端口 8082 上的订单进程,然后再次发起请求。
你会发现部分请求失败,而不是所有流量自动转向 8081。这不是负载均衡失效,而是当前静态实例列表仍然包含已经停止的地址,示例也没有配置健康检查、熔断或重试。
这说明:
- 负载均衡不等于健康检查。
- 健康检查不等于熔断。
- 注册中心也不意味着故障实例会零延迟摘除。
引入动态注册中心之后,还需要理解服务端检测周期、客户端缓存和实例摘除延迟。只把配置从静态地址换成注册中心,并不能消除分布式故障。
五、服务之间如何调用?别把 HTTP 当成本地方法
网关能够访问订单服务后,下一步通常是让订单服务调用库存、价格或支付服务。
常见选择包括:
| 调用方式 | 适用情况 | 需要关注 |
|---|---|---|
| RestClient | 命令式、同步 HTTP 调用 | 连接池、超时、线程占用 |
| WebClient | 响应式调用、流式处理 | 非阻塞链路以及背压处理 |
| Spring HTTP Service Clients | 通过接口描述 HTTP 服务契约 | 底层客户端和代理配置 |
| OpenFeign | 现有 Feign 项目与声明式调用习惯 | 项目维护状态及版本兼容 |
RestClient、WebClient 和 HTTP Service Clients 来自 Spring Framework 的 HTTP 客户端能力,并非全部由 Spring Cloud 提供。
普通客户端也不会因为 URL 中写了服务名,就自动具备注册中心解析能力。使用 Spring Cloud 的服务发现机制时,需要相应的负载均衡集成;使用平台 DNS 时,则由平台负责名称解析。
Spring Cloud OpenFeign 官方已将项目定位为 feature-complete,主要维护缺陷修复,并建议关注 Spring HTTP Service Clients。已有 Feign 项目不必为追新而立即重写,新项目则应把这一维护方向纳入选型。
无论使用哪种客户端,远程调用都有本地调用没有的失败模式:连接失败、响应超时、部分成功,以及调用方不知道下游是否已经提交业务操作。
声明式接口能隐藏 HTTP 细节,不能隐藏网络的不确定性。
六、容错设计:四种机制,别混成一个概念
6.1 超时:不要无限等待
每次远程调用都应有明确的等待边界,至少区分连接超时和响应相关超时,并核对所选客户端对这些配置的具体语义。
更重要的是整条链路的时间预算:
客户端可接受总耗时:2 秒
├── 网关转发与网络开销
├── 订单服务本地处理
├── 库存服务调用
└── 结果组装与响应返回
如果库存单次调用最多耗时 900 毫秒,重试一次又消耗 900 毫秒,再加上 100 毫秒退避,粗略上限已经达到 1.9 秒,几乎没有给其他环节留下空间。
这个估算用于说明预算分配,不应把某个连接或读取超时配置直接当作完整的端到端 Deadline。多阶段操作、重试和线程排队都可能增加总耗时。
6.2 熔断:下游持续失败时,停止继续施压
熔断器通常包含三种状态:
Closed → 失败率或慢调用率达到阈值 → Open
Open → 等待一段时间 → Half-Open
Half-Open → 探测恢复 → Closed
Half-Open → 探测仍失败 → Open
Spring Cloud CircuitBreaker 提供统一抽象,Resilience4j 是常见实现。具体是否启用慢调用统计、阈值如何计算,要看实现和配置。
熔断的作用是快速失败、减少持续冲击,不是把错误变成成功。库存校验失败时,不能降级成“库存充足”;支付结果不确定时,也不能默认返回“支付成功”。
6.3 隔离:一个依赖慢,不要拖垮所有业务
熔断之前,慢请求仍可能占住大量资源,因此还需要限制并发、连接池或线程池的使用。
例如,把报表查询和下单请求放进同一个无界等待队列,报表一变慢,下单也会被堵住。
隔离的重点不是“多建几个线程池”,而是给不同依赖与业务设置明确的资源上限,并定义资源耗尽时如何返回。
6.4 重试:次数越多,不一定越可靠
重试只适合一部分暂时性故障,而且必须受到总时间预算约束。
至少明确四件事:
- 哪些错误可重试,例如短暂连接失败,而不是所有业务错误。
- 最多尝试几次,是否使用退避与随机抖动。
- 网关、客户端和业务代码是否重复配置了重试。
- 写入操作如何保证幂等。
假如网关最多尝试三次,服务客户端也最多尝试三次,下游可能收到九次请求。小故障就这样被放大了。
对于创建订单、扣款、扣库存等操作,应结合业务幂等键、唯一约束和持久化处理状态控制重复执行,而不是只在请求头里放一个字段就宣称完成了幂等。
另外,调用方超时不代表下游停止执行。一次支付请求可能已经完成扣款,只是响应没有及时返回,这正是需要幂等与结果查询机制的原因。
七、分布式一致性:Spring Cloud 不会自动处理
一个服务中的 @Transactional,通常只覆盖它所管理的本地事务资源,不能自动把另一个 HTTP 服务里的数据库操作纳入同一事务。
例如:
订单服务创建订单成功
↓
库存服务扣减库存失败
这时不能期待一个本地事务注解替你跨服务回滚。
一种常见设计是:
- 订单服务在本地事务中同时写入订单和 Outbox 事件。
- 独立投递流程把事件发送到消息中间件。
- 库存服务消费事件,通过业务约束和消费记录实现幂等。
- 成功或失败结果推动订单状态机继续流转。
- 对长时间未完成的状态执行查询、补偿或人工处理。
Outbox 主要解决本地数据库更新与事件记录的一致性,不代表消息只会投递一次。投递重试、重复消费、乱序和补偿仍需设计。
是否采用 Saga、TCC 或其他事务方案,应根据业务一致性要求、参与方能力与复杂度成本判断,而不是看到多个服务就先上一个分布式事务框架。
八、配置与可观测性:应用启动只是起点
8.1 配置中心不是“远程 application.yml”那么简单
集中配置的价值不仅是减少重复文件,还包括环境隔离、版本追踪和变更管理。
使用 Spring Cloud Config 时,现代 Spring Boot 应用通常通过 Config Data 机制接入。配置变化是否能刷新到现有对象,则取决于属性绑定、Bean 生命周期和相关刷新机制,不能认为“配置中心改了,所有连接池就自动重建了”。
配置变更应重点区分:
- 哪些可以动态调整,例如部分业务开关。
- 哪些需要验证后逐步发布,例如限流阈值。
- 哪些更适合重启生效,例如影响基础设施初始化的参数。
- 哪些属于凭证,应交给相应的秘密管理机制。
一次错误配置能同时影响多个实例,因此配置发布也需要审核、灰度与回滚,而不是只有代码发布才走流程。
8.2 日志、指标、链路追踪各司其职
| 信号 | 主要回答的问题 | 常见内容 |
|---|---|---|
| 日志 | 某一次事件具体发生了什么? | 业务标识、异常上下文、Trace ID |
| 指标 | 系统整体表现如何? | 请求量、错误率、P95/P99 延迟、资源饱和度 |
| 链路追踪 | 一次请求经过了哪里,时间花在哪? | Span、上下游关系、跨服务耗时 |
现代 Spring Boot 应用通常以 Actuator、Micrometer Observation/Tracing 等能力为基础,再对接相应的指标与追踪后端。旧版 Spring Cloud Sleuth 不应直接作为新版本项目的默认依赖。
落地时还要注意:
- Trace 上下文必须沿 HTTP 或消息链路传播,单独打印一个随机 ID 并不等于分布式追踪。
- 指标标签应保持可控基数,订单号和用户 ID 不适合直接用作普通指标标签。
- 日志应处理敏感字段,管理端点应限制暴露范围和访问权限。
- 告警应关注错误率、延迟和饱和度,而不是只判断进程是否还活着。
真正有用的可观测性,是出现“用户说下单很慢”时,你能定位到具体环节,而不是收集了一堆没人能解释的数据。
九、常见误区与排查清单
9.1 把旧版组件清单当成当前推荐
阅读较早的文章时,经常会遇到下面这些组合:
| 旧资料中的组件 | 当前项目应关注的方向 |
|---|---|
| Ribbon | Spring Cloud LoadBalancer |
| Hystrix | Spring Cloud CircuitBreaker 与适合项目的实现 |
| Zuul 1 | Spring Cloud Gateway,按 WebFlux 或 Web MVC 选择实现 |
| Spring Cloud Sleuth | Micrometer Tracing 等现代观测集成 |
这些是能力演进方向,不是修改一个 Maven 坐标就能完成的直接替换。线程模型、配置方式和运行行为都需要重新核对。
Eureka 不能因为同属早期 Netflix 生态,就和上述所有组件一起被笼统判为不可用。应分别查看每个项目的维护与兼容状态。
9.2 网关启动了,但请求没有正确转发
按下面的顺序查,比反复重启有效:
- 检查 Starter 与运行模型是否匹配,是否混入不需要的 MVC 依赖。
- 检查路由配置前缀是否符合当前 Gateway 版本。
- 检查
Path是否匹配,以及StripPrefix后的路径是否符合下游接口。 - 检查服务名是否与发现列表一致,以及是否引入 LoadBalancer。
- 直接访问下游实例,区分路由问题和服务自身问题。
- 根据网关日志、下游日志与追踪信息确认错误发生在哪一层。
404 既可能来自没有匹配路由,也可能来自下游路径错误;服务发现没有可用实例时,默认情况下可能返回 503;连接失败则需要继续检查目标进程和网络。不要只凭一个状态码就锁定根因。
9.3 服务越拆越多,发布却越来越难
如果一次小改动总要同时发布五个服务,或者服务之间相互读取对方的数据库表,说明拆分边界和契约可能存在问题。
可以优先检查:
- 服务是否围绕业务能力拆分,而不是按 Controller、Service、DAO 分层拆分。
- API 与消息契约能否向后兼容。
- 数据所有权是否清楚。
- 跨服务同步调用链是否过长。
- 是否具备契约测试、集成测试和独立回滚能力。
多个进程之间高度耦合,通常只会得到一个更难排查的“分布式单体”。
十、总结:把能力接起来,更要把边界想清楚
理解 Spring Cloud,可以抓住这条主线:
发现服务 → 选择实例 → 发起调用 → 控制失败 → 观察行为
在此基础上,再按业务需要加入集中配置、消息驱动和一致性设计。
本文的网关与双实例示例展示了最小的服务协作链路,但真正的工程质量来自链路之外的约束:版本兼容、超时预算、幂等机制、资源隔离、契约管理,以及明确的业务边界。
Spring Cloud 的价值,不是让系统拥有更多组件,而是让必须分布式协作的系统更可控。
先让一条链路清楚地运行,再让它在故障中保持可解释、可恢复,最后才是把规模做大。
参考资料
可根据以下资料名称检索官方文档,核对版本关系、组件定位和配置细节。
- Spring Cloud 官方项目页与版本兼容表
- Spring Cloud 已支持版本说明
- Spring Cloud 2025.1.2 BOM
- Spring Cloud Gateway 5.0:WebFlux 路由配置
- Spring Cloud Gateway 5.0:HTTP 超时配置
- Spring Cloud Commons 5.0:服务发现与 SimpleDiscoveryClient
- Spring Cloud OpenFeign:项目维护定位
- Spring Cloud CircuitBreaker
- Spring Cloud Config
- Spring Framework HTTP Service Clients
- Spring Boot 可观测性
- 点赞
- 收藏
- 关注作者
评论(0)