为什么Spring Cloud Gateway路由很慢?是否是我的配置存在问题?
问题排查与解决思路
1. 优先排查HttpClient连接配置问题
你使用的Spring Cloud 2020.0.3版本默认基于Reactor Netty实现Http客户端转发,HTTPS场景下如果没有配置连接池复用、SSL会话缓存、DNS缓存,每次请求都会重复执行TCP握手、SSL握手、DNS解析,单这部分开销就可能达到几百ms,刚好符合你遇到的700ms左右的耗时差。
可以通过自定义HttpClient优化连接相关配置:
import io.netty.channel.ChannelOption; import io.netty.handler.ssl.SslContextBuilder; import org.springframework.cloud.gateway.config.HttpClientCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import reactor.netty.http.client.HttpClient; import java.time.Duration; @Configuration public class HttpClientConfig { @Bean public HttpClientCustomizer httpClientCustomizer() { return httpClient -> httpClient // 连接超时时间配置 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .responseTimeout(Duration.ofMillis(15000)) // 延长DNS缓存时间,避免频繁解析域名 .resolver(dnsSpec -> dnsSpec .queryTimeout(Duration.ofMillis(3000)) .cacheMaxTimeToLive(Duration.ofMinutes(30)) ) // 配置SSL会话缓存,复用HTTPS连接 .secure(sslSpec -> sslSpec.sslContext( SslContextBuilder.forClient() .sessionCacheSize(2000) .sessionTimeout(Duration.ofHours(1).toSeconds()) )); } }
配置完成后多次调用接口测试平均耗时,排除首次初始化冷启动的影响。
2. 关闭不必要的内置过滤器
Spring Cloud Gateway默认开启了部分全局过滤器,如果不需要可以手动关闭,减少不必要的性能损耗:
- 关闭请求体缓存:如果没有修改请求体的需求,在配置文件中添加
spring.cloud.gateway.filter.adapt-cached-body.enabled=false,该过滤器会把请求体全量读入内存再转发,对大请求耗时影响极大 - 关闭调试日志:生产环境关闭
spring.cloud.gateway.httpclient.wiretap=false,避免全量打印请求响应内容带来的IO开销
3. 验证自定义断言的执行耗时
虽然你提到断言中没有IO操作,但如果存在大量正则匹配、字符串遍历、集合遍历逻辑,也可能产生额外耗时。可以在自定义断言WrapperAPIPredicateFactory的逻辑前后加耗时埋点,确认该部分的执行时间是否符合预期。
4. 排除网络层面差异
在网关部署的机器上直接curl调用platform服务的接口,对比和本地直接调用的耗时差:
- 如果网关机器上直接调用就有很高的耗时,说明是网关所在机器到platform服务的网络问题,和网关本身无关
- 如果直接调用耗时正常,再排查网关的JVM运行状态,确认是否存在频繁GC、内存不足的问题。
5. 全链路耗时埋点定位
可以添加全局耗时过滤器,定位耗时具体产生在网关的哪个阶段:
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class TimingGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { long start = System.currentTimeMillis(); return chain.filter(exchange).then(Mono.fromRunnable(() -> { long totalCost = System.currentTimeMillis() - start; // 可根据需求拆分打印路由匹配、断言执行、转发请求各阶段的耗时 System.out.printf("请求路径%s,网关总耗时%dms%n", exchange.getRequest().getPath(), totalCost); })); } @Override public int getOrder() { // 设为最高优先级,统计全链路耗时 return Ordered.HIGHEST_PRECEDENCE; } }
内容的提问来源于stack exchange,提问作者Pokuri
相关产品推荐
相关产品推荐

