Spring Cloud Gateway 2.0.0 M9响应缓存实现可行性咨询
嘿,针对你在Spring Cloud Gateway 2.0.0 M9中想要实现类似代理缓存的响应缓存需求(支持带参数的URL),我来分享下可行的方案——就像你用Hystrix实现熔断那样,我们可以通过自定义GatewayFilter来搞定这个需求:
核心思路
Spring Cloud Gateway本身并没有内置的代理缓存过滤器,但它的扩展机制允许我们自定义过滤器来实现缓存逻辑。我们可以借助Spring的Cache抽象层(支持Caffeine、Redis等多种缓存实现),将请求的「路径+有序参数」作为缓存Key,实现对带参数URL的差异化缓存。
具体实现步骤
1. 添加依赖
首先引入Spring Cache和缓存实现(这里以轻量的Caffeine为例):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency>
2. 自定义缓存过滤器工厂
我们需要实现一个GatewayFilterFactory,用来处理缓存的读取和写入逻辑:
import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; import java.util.List; import java.util.stream.Collectors; @Component public class CacheResponseGatewayFilterFactory extends AbstractGatewayFilterFactory<CacheResponseGatewayFilterFactory.Config> { private final CacheManager cacheManager; public CacheResponseGatewayFilterFactory(CacheManager cacheManager) { super(Config.class); this.cacheManager = cacheManager; } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { // 生成缓存Key:路径+按Key排序的参数(避免参数顺序不同导致缓存Key不一致) String path = exchange.getRequest().getPath().toString(); String queryParams = exchange.getRequest().getQueryParams().entrySet().stream() .sorted(java.util.Map.Entry.comparingByKey()) .map(entry -> entry.getKey() + "=" + String.join(",", entry.getValue())) .collect(Collectors.joining("&")); String cacheKey = path + (queryParams.isEmpty() ? "" : "?" + queryParams); Cache cache = cacheManager.getCache(config.getCacheName()); if (cache != null) { // 尝试从缓存获取响应 byte[] cachedResponse = cache.get(cacheKey, byte[].class); if (cachedResponse != null) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.OK); response.getHeaders().add("X-Cache", "HIT"); return response.writeWith(Mono.just(response.bufferFactory().wrap(cachedResponse))); } } // 缓存未命中,继续执行路由,然后缓存成功的响应 return chain.filter(exchange).then(Mono.fromRunnable(() -> { exchange.getResponse().getBody().subscribe(buffer -> { byte[] responseBytes = new byte[buffer.readableByteCount()]; buffer.read(responseBytes); if (cache != null && exchange.getResponse().getStatusCode().is2xxSuccessful()) { cache.put(cacheKey, responseBytes); exchange.getResponse().getHeaders().add("X-Cache", "MISS"); } }); })); }; } // 配置类,用于指定缓存名称 public static class Config { private String cacheName; public String getCacheName() { return cacheName; } public void setCacheName(String cacheName) { this.cacheName = cacheName; } } }
3. 在路由中启用缓存过滤器
在你的RouteLocator配置里,添加自定义的缓存过滤器,就像配置Hystrix过滤器那样:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import com.github.benmanes.caffeine.cache.Caffeine; import java.util.concurrent.TimeUnit; @SpringBootApplication @EnableCaching // 启用Spring Cache public class WorkflowGatewayApiApplication { public static void main(String[] args) { SpringApplication.run(WorkflowGatewayApiApplication.class, args); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/api/**") // 应用缓存过滤器,指定缓存名称 .filters(f -> f.filter(new CacheResponseGatewayFilterFactory(cacheManager()).apply( config -> config.setCacheName("apiResponseCache")))) .uri("http://your-backend-service")) // 替换成你的后端服务地址 .build(); } // 配置Caffeine缓存的过期策略和容量 @Bean public CaffeineCacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager("apiResponseCache"); cacheManager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) // 缓存10分钟后过期 .maximumSize(1000)); // 最多缓存1000条响应 return cacheManager; } }
关键注意事项
- 参数排序处理:代码中对请求参数按Key排序后拼接,确保
/api/data?id=1&name=foo和/api/data?name=foo&id=1会命中同一个缓存条目。 - 缓存范围控制:只缓存2xx状态码的成功响应,避免缓存错误结果影响业务。
- 缓存实现切换:如果需要分布式缓存,可以把
CaffeineCacheManager换成RedisCacheManager,只需调整依赖和配置即可。 - 响应体处理:因为Spring Cloud Gateway是基于Reactive的,所以用响应式的方式读取和写入缓存,避免阻塞。
这种自定义过滤器的方式和你用Hystrix实现熔断的思路一致,都是利用Gateway的扩展机制来增强功能,完全适配你当前使用的2.0.0 M9版本。
内容的提问来源于stack exchange,提问作者Esteban S
相关产品推荐
相关产品推荐

