Spring Cloud Gateway 动态路由与全局过滤器问题排查
问题背景
在使用 Spring Cloud Gateway 构建 API 网关时,实现了两个核心 GlobalFilter
:
- **
DynamicRouterFilter
**:根据请求头中的 REAL_URL
动态转发请求
- **
CustomGlobalFilter
**:实现鉴权、签名校验、限流、调用次数扣减及响应日志记录
但在实际运行中遇到两个关键问题:
- 请求未正确转发到下游服务
- 响应体日志未打印(
handleResponse
未生效)
原代码实现
DynamicRouterFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Slf4j @Component public class DynamicRouterFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); if (!headers.containsKey(REAL_URL)){ return handleError(exchange.getResponse(), HttpStatus.BAD_REQUEST, "无效的请求地址"); } String realUrl = headers.getFirst(REAL_URL); if(StringUtils.isBlank(realUrl)){ return handleError(exchange.getResponse(), HttpStatus.BAD_REQUEST, "无效的请求地址"); }
try { URI uri = getUri(realUrl, request); log.info("动态路由转发: {}", uri); exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri); ServerHttpRequest newRequest = request.mutate() .uri(uri) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); } catch (URISyntaxException e) { return handleError(exchange.getResponse(), HttpStatus.BAD_REQUEST, "无效的请求地址"); } } private static URI getUri(String realUrl, ServerHttpRequest request) throws URISyntaxException {}
@Override public int getOrder() { return 1; } }
|
CustomGlobalFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| @Slf4j @Component public class CustomGlobalFilter implements GlobalFilter, Ordered { public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { .... ServerHttpResponse response = exchange.getResponse();
...... ServerHttpResponseDecorator decoratedResponse = handleResponse(response); return chain.filter(exchange.mutate().response(decoratedResponse).build()); }
@Override public int getOrder() { return 0; }
public ServerHttpResponseDecorator handleResponse(ServerHttpResponse response) { ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux) { Flux<DataBuffer> fluxBody = (Flux<DataBuffer>) body; return DataBufferUtils.join(fluxBody).flatMap(dataBuffer -> { try { byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); String responseBody = new String(content, StandardCharsets.UTF_8); .... DataBuffer newBuffer = response.bufferFactory().wrap(content); DataBufferUtils.release(dataBuffer); return super.writeWith(Mono.just(newBuffer)); } catch (Exception e) { log.error("处理响应体时出错", e); return handleError(response); } }) } return super.writeWith(body); } }; return decoratedResponse; } }
|
问题一:请求未转发到下游服务
现象
- 网关日志显示
DynamicRouterFilter
成功设置了目标 URI(如 http://localhost:8123/api/name/user
)
- 但下游目标服务未收到请求
- 网关最终返回
200
或 404
,且 routeUri=no://op
原因
Spring Cloud Gateway 内置的 RouteToRequestUrlFilter
(order = 10000)在 DynamicRouterFilter
之后执行,并覆盖了我们手动设置的 GATEWAY_REQUEST_URL_ATTR
即使设置了正确的 URI,RouteToRequestUrlFilter
仍会根据 route 配置中的 uri: no://op
重新生成无效 URL
解决方案
确保 DynamicRouterFilter
在 RouteToRequestUrlFilter
之后执行,从而覆盖其结果:
1 2 3 4 5 6 7
| @Component public class DynamicRouterFilter implements GlobalFilter, Ordered { @Override public int getOrder() { return 20000; } }
|
同时,确保 route 配置存在(即使 uri 是占位符):
1 2 3 4 5 6 7 8
| spring: cloud: gateway: routes: - id: dynamic-route uri: http://placeholder predicates: - Path=/**
|
问题二:响应日志未打印
现象
CustomGlobalFilter.handleResponse()
中的 log.info("响应体: ...")
未执行
根本原因
1. 响应装饰器未生效
NettyWriteResponseFilter
(order = -1)在 CustomGlobalFilter
(order = 0)之前执行,提前锁定了响应流,在之后包装的 ServerHttpResponseDecorator
被忽略
解决方案
1. 调整 Filter 执行顺序
让 CustomGlobalFilter
在 NettyWriteResponseFilter
之前执行:
1 2 3 4
| @Override public int getOrder() { return -2; }
|