You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux网关:直接返回Redis缓存响应时的异常与链路问题

问题分析与解决方案

你的问题存在两个核心原因:

  1. 使用同步Redis操作破坏了Reactive流的非阻塞特性,导致过滤器流程异常,后续仍会触发后端控制器请求
  2. 返回缓存响应后未标记请求已处理,后续NettyRoutingFilter仍尝试修改响应头,引发UnsupportedOperationException

以下是针对性的修复步骤:

1. 替换为ReactiveRedisTemplate

Spring Cloud Gateway是Reactive环境,必须使用ReactiveRedisTemplate而非同步的RedisTemplate,避免线程阻塞和流程错乱。

首先注入ReactiveRedisTemplate:

private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;

public YourFilter(ReactiveRedisTemplate<String, String> reactiveRedisTemplate) {
    this.reactiveRedisTemplate = reactiveRedisTemplate;
}

2. 修正缓存Key查询逻辑

将同步的keys/multiGet替换为Reactive方法(生产环境建议用scan替代keys,避免阻塞Redis):

private Flux<String> generateCacheKey(String cacheKey) {
    return reactiveRedisTemplate.keys("*" + cacheKey + "*");
}

3. 重写过滤器核心逻辑

调整流程为全Reactive操作,并在返回缓存时标记请求已处理,跳过后续路由过滤器:

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();

    String api = apis.keySet().stream()
            .filter(e -> request.getURI().getPath().endsWith(e))
            .findFirst()
            .orElse(null);

    if (api == null) {
        return chain.filter(exchange);
    }

    return ReactiveSecurityContextHolder.getContext()
            .flatMap(sc -> {
                Authentication authentication = sc.getAuthentication();
                if (authentication == null || authentication.getPrincipal() == null) {
                    return chain.filter(exchange);
                }

                String cacheKey = apis.get(api);
                return generateCacheKey(cacheKey)
                        .next() // 取第一个匹配的缓存Key
                        .flatMap(key -> reactiveRedisTemplate.opsForValue().get(key))
                        .flatMap(cachedResponse -> {
                            if (cachedResponse != null) {
                                // 标记请求已处理,跳过后续路由过滤器
                                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR, true);
                                return writeCachedResponse(response, cachedResponse);
                            } else {
                                return chain.filter(exchange);
                            }
                        })
                        .switchIfEmpty(chain.filter(exchange));
            })
            .switchIfEmpty(chain.filter(exchange))
            .doOnError(e -> log.error("Error during filter execution", e));
}

4. 完善响应写入方法

设置必要的响应头,确保响应正确提交:

private Mono<Void> writeCachedResponse(ServerHttpResponse response, String cachedResponse) {
    // 根据实际业务调整Content-Type
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
    response.setStatusCode(HttpStatus.OK);
    
    DataBuffer buffer = response.bufferFactory().wrap(cachedResponse.getBytes(StandardCharsets.UTF_8));
    return response.writeWith(Mono.just(buffer))
            .doOnSuccess(v -> response.setComplete()); // 标记响应处理完成
}

5. 调整过滤器执行顺序

确保你的过滤器在NettyRoutingFilter之前执行(默认顺序为-1),通过@Order设置优先级:

@Component
@Order(0) // 顺序大于-1,保证在路由过滤器之前拦截
public class YourFilter implements GatewayFilter, Ordered {
    // ... 其他代码 ...

    @Override
    public int getOrder() {
        return 0;
    }
}

修复说明

  • 全Reactive操作保证了网关的非阻塞特性,避免同步调用导致的流程异常
  • GATEWAY_ALREADY_ROUTED_ATTR标记会让Spring Cloud Gateway跳过后续路由逻辑,彻底终止过滤链路
  • 完善响应头和标记响应完成,避免后续过滤器尝试修改已提交的响应

内容的提问来源于stack exchange,提问作者elhetamy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:38:24