spring cloud gateway性能优化

spring cloud 网关性能瓶颈分析及优化

背景

在公司的开放平台中需要对外保留接口,此时需要一个公共层来处理公共逻辑,限流,验证权限,所以需要一个网关

在这里插入图片描述

技术选型

在这里插入图片描述

此处从网上贴图

网关整体设计 及架构

在这里插入图片描述
在这里插入图片描述

优化背景

在上线之后,对接服务商增多,挂载接口增多的情况下,需要测试性能瓶颈。此时发现和官方给出的数据差别较大

直接压测

在这里插入图片描述

去掉日志之后

在这里插入图片描述

性能有提升,但是还是不符合预期,此时观察到jvm的情况,YGC次数太多

在这里插入图片描述

修改jvm参数

在这里插入图片描述

此时性能提升,但是还不满意,继续探索

怀疑是路由数量导致的问题,进行验证

在这里插入图片描述

验证之后,确实是路由数量会导致吞吐量的下降,进行代码分析

性能瓶颈代码分析

在这里插入图片描述

DispatcherHandler :接收到请求,匹配 HandlerMapping ,此处会匹配到
RoutePredicateHandlerMapping 。
RoutePredicateHandlerMapping :接收到请求,匹配 Route 。

读RoutePredicateHandlerMapping 源码发现:获取具体的路由会:
调用 RouteLocator#getRoutes() 方法,获得全部 Route ,
并调用 Predicate#test(ServerWebExchange) 方法,顺序匹配一个 Route。

在这里插入图片描述

此时会遍历缓存中的路由列表,然后获取每一个路由的断言器
,将请求信息传入断言器工厂,去判断是否和当前的路由匹配,
如果情况最糟糕时候,最终匹配到的路由排在
最后一个,此处就会出现性能问题

改造方式1

从官网看到一个修改的思路,结合我们SCG对Api的设计,因为路由和Api是一一对应的,知道Api其实已经可以知道路由信息了,不用走gateway自己那套功能强大但性能有损耗的路由规则解析,我们何不在构建路由信息的时候同时增加一个缓存Map,key为路由ID,这个ID是可以自定义的,我们可以让Api的唯一标识作为路由的ID,Map的value就是路由的信息,这样当请求过来,根据我们的协议规则可以解析出Api,有了Api就可以通过这个Map拿到对应的路由对象了,可以绕过RoutePredicateHandlerMapping#lookupRoute的原有逻辑。

将原有的挨个匹配改为key-value的精准匹配,所以需要修改一下源代码,官方已经给出了修改方法,结合我们的情况,觉得重写一下底层的RoutePredicateHandlerMapping和CachingRouteLocator

修改CachingRouteLocator代码如下:

在这里插入图片描述

RoutePredicateHandlerMapping代码修改

在这里插入图片描述

增加以下代码

在这里插入图片描述

修改完成之后,逻辑就是在原有的路由保存代码中,增加一个新的map缓存,key为路由ID,在匹配代码前从请求中获取路由的唯一标识,路由ID,然后精准匹配,匹配不到走原有逻辑
在这里插入图片描述

改造方式2

在这里插入图片描述

这部分代码不展示了,只说实现的逻辑,改造完成之后的结果

在这里插入图片描述

整个优化过程结果展示

在这里插入图片描述

改造思路3

在这里插入图片描述

这个只是一个方向,本人尚未实现

### Spring Cloud Gateway 性能优化方法 #### 1. 使用异步非阻塞编程模型 为了提高系统的吞吐量并减少延迟,采用异步非阻塞的方式处理请求是非常重要的。Spring WebFlux 提供了一个响应式的框架来构建基于事件驱动的应用程序,这有助于更好地利用 CPU 和内存资源[^1]。 ```java @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(r -> r.path("/async/**") .filters(f -> f.rewritePath("/async/(?<segment>.*)", "/${segment}") .addResponseHeader("X-Async-Request", "true")) .uri("lb://service-name")) .build(); } ``` #### 2. 配置合理的线程池大小 调整 Reactor 的调度器配置可以显著影响应用的表现。默认情况下,Reactor 使用的是弹性调度器(Elastic Scheduler),但在生产环境中可能需要自定义更合适的调度策略以适应特定的工作负载特性[^2]。 ```yaml spring: main: web-application-type: reactive reactor: thread-pool: core-size: 8 max-size: 64 ``` #### 3. 启用压缩功能 对于 HTTP 响应数据启用 GZIP 或其他形式的内容编码能够有效降低网络传输时间,从而加快页面加载速度以及改善用户体验。可以通过设置 `server.compression` 属性轻松实现这一点。 ```yaml server: compression: enabled: true mime-types: application/json,application/xml,text/html,text/css,application/javascript,image/svg+xml min-response-size: 1024B ``` #### 4. 实施缓存机制 合理运用 Redis 或 Ehcache 等分布式缓存解决方案可以在一定程度上减轻后端服务的压力,并且加速频繁访问的数据获取过程。通过在网关层面加入缓存过滤器,可避免不必要的重复查询操作。 ```java @Configuration public class CacheConfig { @Bean public CaffeineCacheManager caffeineCacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager("apiResponses"); cacheManager.setCaffeine(caffeineSpec()); return cacheManager; } private Caffeine<Object, Object> caffeineSpec() { return Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).maximumSize(100); } } ``` #### 5. 设置适当的超时参数 为了避免长时间挂起的连接占用过多服务器资源,在实际部署过程中应当仔细考虑各种类型的超时时长设定,比如读取/写入超时、连接建立超时等。这些都可以通过对 WebClient 进行定制化配置完成。 ```java @Bean public HttpClient httpClient() { return HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .responseTimeout(Duration.ofMillis(5000)) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5, TimeUnit.SECONDS)) .addHandlerLast(new WriteTimeoutHandler(5, TimeUnit.SECONDS))); } @Bean public WebClient.Builder webClientBuilder(HttpClient httpClient) { return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)); } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值