Spring Cloud Gateway 实战:构建高可用微服务 API 网关的五个关键设计
在微服务架构演进中,服务拆分带来了独立部署与弹性伸缩的红利,也引入了一个绕不开的问题:外部客户端该如何与数十乃至上百个内部服务通信?直接暴露所有服务显然不可行,API 网关因此成为微服务体系的"统一入口"。Spring Cloud Gateway 基于 Spring WebFlux 与 Reactor 实现非阻塞 IO,在吞吐与资源占用上相较传统 Zuul 1.x 有数量级提升。本文围绕路由、鉴权、限流、熔断、统一响应五个关键设计,给出一套可落地的工程实践。
一、动态路由:配置即代码
网关最核心的职责是请求转发。Spring Cloud Gateway 采用"断言 + 过滤器"模型描述路由,推荐将路由配置外置到 Nacos,实现不重启网关即可调整转发规则。
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
lb:// 前缀结合服务发现实现负载均衡;StripPrefix=1 去掉 /api 前缀再转发;Retry 过滤器对网关层超时自动重试,避免上游偶发抖动直接透传给调用方。配合 Nacos 配置中心监听变更,可做到路由热更新。
二、全局过滤器:统一鉴权与上下文传递
业务服务不应重复实现鉴权逻辑。通过 GlobalFilter 在网关层完成 Token 校验,并将解析出的用户身份写入下游请求头,实现一次校验、处处可用。
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(token)) {
return unauthorized(exchange, "缺少认证令牌");
}
return jwtVerifier.verify(token)
.flatMap(claims -> {
ServerHttpRequest req = exchange.getRequest().mutate()
.header("X-User-Id", claims.getSubject())
.header("X-Tenant", claims.get("tenant", String.class))
.build();
return chain.filter(exchange.mutate().request(req).build());
})
.onErrorResume(e -> unauthorized(exchange, "令牌无效或已过期"));
}
@Override
public int getOrder() { return -100; }
}
getOrder() 返回负值确保该过滤器在路由转发前执行。鉴权通过后以 mutate() 构造新请求注入身份头,下游服务直接读取即可,无需再次解析 JWT。
三、令牌桶限流:保护脆弱上游
突发流量可能压垮下游。Spring Cloud Gateway 内置 RequestRateLimiter 过滤器,基于 Redis 令牌桶算法实现分布式限流。
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-User-Id")
);
}
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 20
redis-rate-limiter.burstCapacity: 40
key-resolver: "#{@userKeyResolver}"
replenishRate 为每秒令牌填充数,burstCapacity 为桶容量。按用户维度限流,既防止单用户滥用,又避免全局阈值误伤正常业务。被限流时网关返回 429,前端可据此展示友好提示而非白屏。
四、熔断降级:快速失败优于级联等待
当下游服务不可用时,网关若同步等待超时,会快速耗尽自身线程池引发雪崩。集成 Resilience4j 熔断器,在错误率超阈值时直接走降级逻辑。
filters:
- name: CircuitBreaker
args:
name: userCircuit
fallbackUri: forward:/fallback/users
@RestController
@RequestMapping("/fallback")
public class FallbackController {
@GetMapping("/users")
public Mono<Map<String, Object>> usersFallback() {
return Mono.just(Map.of(
"code", 503, "message", "用户服务暂不可用,请稍后重试"
));
}
}
熔断打开后请求被重定向到 fallbackUri,返回兜底数据而非异常。配合 Actuator 暴露的 /actuator/circuitbreakers 端点,可实时观测各熔断器状态,为容量规划提供数据支撑。
五、统一响应与全局异常处理
不同服务返回结构各异,前端需适配多种格式,维护成本高。在网关层用 modifyResponseBody 统一包装,并对异常统一编码。
@Component
public class ResponseWrapperFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse resp = exchange.getResponse();
if (resp.getStatusCode().is2xxSuccessful()) {
// 统一为 {code, data, message} 结构
}
}));
}
}
至此,客户端只需对接一套响应协议,后端服务各自演进互不影响。
结语
API 网关是微服务治理的集大成者:路由解决"去哪",鉴权解决"谁能进",限流与熔断解决"扛得住",统一响应解决"好对接"。五个设计环环相扣,共同把横切关注点从业务服务中剥离,让业务代码回归纯粹。实践中建议为网关单独建立监控大盘,关注 P99 延迟、熔断状态与限流命中率三项核心指标,方能真正驾驭这套统一入口。
- 点赞
- 收藏
- 关注作者
评论(0)