写点什么

SpringCloud Gateway 路由转发性能优化

用户头像
中原银行
关注
发布于: 2021 年 06 月 17 日
SpringCloud Gateway 路由转发性能优化

SpringCloud Gateway 系列文章共五篇,由我行开发工程师 @Aaron 提供,带大家深入剖析 Gateway 工作原理,及如何基于 Gateway 进行定制化开发以适应企业特定环境需求。


第一篇:SpringCloud Gateway 动态路由

第二篇:SpringCloud Gateway 路由数量对性能的影响研究

第三篇:SpringCloud Gateway 路由转发性能优化

第四篇:SpringCloud Gateway 路由断言。

第五篇:SpringCloud Gateway 过滤器。


前面篇章,通过测试验证,发现随着路由增长,路由性能会严重下降。

本篇,针对采用 Path 方式路由的进行性能优化,注意该【优化】仅适用于特定场景,不具备普适性

源码解读

RoutePredicateHandlerMapping类是 SpringCloud Gateway 接收 Web 请求,并查找匹配路由,具体方法为:

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {...}
复制代码

对源码做简单修改,比如,Path 匹配 /mock/** 则对路由查找结果进行缓存(注意这里缓存策略和方式仅仅是举例,根据实际需求情况来做)

public static final String MOCK_PATCH = "/mock/**";private Map<String, Route> hashCache = new ConcurrentHashMap<>(1024);protected Mono<Route> lookupRoute(ServerWebExchange exchange) {  String path = exchange.getRequest().getPath().subPath(0).value();  //符合Path规则,优先从缓存Map获取,时间复杂度近似于O(1)  if (pathMatcher.match(MOCK_PATCH, path)) {    return Mono.justOrEmpty(hashCache.get(path))               .switchIfEmpty(getRouteMono(exchange, path));  }  return getRouteMono(exchange, path);}private Mono<Route> getRouteMono(ServerWebExchange exchange, String path) {  return this.routeLocator.getRoutes()      //... 略过      .map(route -> {        if (logger.isDebugEnabled()) {          logger.debug("Route matched: " + route.getId());        }        validateRoute(route, exchange);        //符合Path规则,缓存路由        if (pathMatcher.match(MOCK_PATCH, path)) {          hashCache.put(path, route);        }        return route;      });}
复制代码

继续查阅源码,找到RoutePredicateHandlerMapping 是如何装配的。在GatewayAutoConfiguration 中实现了 SpringCloud Gateway 内部组件的自动装配,RoutePredicateHandlerMapping 也在其中,代码入下:

@Beanpublic RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,    RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {  return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);}
复制代码

很遗憾,官方没有给这个自动装配添加条件,我们无法自行装配替代默认装配。

我们只能采取以下步骤:

  1. 在 Springboot 启动类上增加排除 GatewayAutoConfiguration 的自动装配配置;

  2. 继承 GatewayAutoConfiguration 并完全拷贝其装配条件;

  3. 覆盖父类 routePredicateHandlerMapping方法,给装配添加条件;

  4. 继承RoutePredicateHandlerMapping ,覆盖其 lookupRoute方法,符合一定条件的请求,优先从缓存中查找路由。

改造 Gateway

修改启动类,排除自动装配

@SpringBootApplication(exclude = GatewayConfiguration.class)public class GatewayApplication {  public static void main(String[] args) {    SpringApplication.run(GatewayApplication.class, args);  }}
复制代码

继承 GatewayAutoConfiguration

@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)@EnableConfigurationProperties@AutoConfigureBefore({HttpHandlerAutoConfiguration.class,    WebFluxAutoConfiguration.class})@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class,    GatewayClassPathWarningAutoConfiguration.class})@ConditionalOnClass(DispatcherHandler.class)public class CustomGatewayAutoConfiguration extends GatewayAutoConfiguration {  // 实现自定义的RoutePredicateHandlerMapping装配  @Bean  public CustomRoutePredicateHandlerMapping customRoutePredicateHandlerMapping(      // 通过@Qualifier 制定装配的缓存管理器      @Qualifier("routeCacheManager")          CacheManager routeCacheManager,      FilteringWebHandler webHandler, RouteLocator routeLocator,      GlobalCorsProperties globalCorsProperties, Environment environment) {    return new CustomRoutePredicateHandlerMapping(        cacheManager, webHandler, routeLocator, globalCorsProperties, environment);  }  // 覆盖父类同名方法,增加使之失效的条件  @Bean  @ConditionalOnMissingBean(RoutePredicateHandlerMapping.class)  public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,      RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties,      Environment environment) {    return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties,        environment);  }}
复制代码

继承 RoutePredicateHandlerMapping

public class CustomRoutePredicateHandlerMapping extends RoutePredicateHandlerMapping {  private final Cache specialCache;  public CustomRoutePredicateHandlerMapping(      CacheManager cacheManager,      FilteringWebHandler webHandler,      RouteLocator routeLocator,      GlobalCorsProperties globalCorsProperties,      Environment environment) {    super(webHandler, routeLocator, globalCorsProperties, environment);    specialCache = cacheManager.getCache("specialRouteCache");  }  @Override  protected Mono<Route> lookupRoute(ServerWebExchange exchange) {    //1. 从exchange中获取请求特征,如path    //2. 如果符合特征 则使用缓存,从缓存获取,如果缓存未命中,    //   调用 super.lookupRoute(exchange) 并加入缓存    //3. 不符合特征的,直接调用    // 下面演示使用 caffeine 缓存的方式    String specialPath = exchange.getRequest().getPath().subPath(0).value();    // 判断path是否符合缓存规则(一般而言用于仅采用Path断言,或简单结合header或query的情况,下面以只有path为例)    if (checkPath(specialPath)) {      return CacheMono          // 查找缓存          .lookup(              key -> Mono.justOrEmpty(specialCache.get(key, Route.class)).map(Signal::next),              toKey(specialPath))          // 未命中直接查找路由表          .onCacheMissResume(              () -> super.lookupRoute(exchange))          // 然后写到缓存          .andWriteWith(              (key, signal) -> Mono.fromRunnable(                  () -> Optional.ofNullable(signal.get())                      .ifPresent(value -> specialCache.put(key, value))              ));    }    return super.lookupRoute(exchange);  }  /**   * 校验请求特征的方法,此处仅是举例   */  private boolean checkPath(String path) {    return true;  }  /**   * 生成cacheKey的方式,此处仅是举例   */  private String toKey(String specialPath) {    return specialPath;  }}
复制代码

缓存管理配置(注意这里不宜采用 Redis 做缓存,因其性能会较低)

@Configuration@AutoConfigureBefore(CustomGatewayAutoConfiguration.class)public class CacheManagerConfiguration {  @Bean  @Primary  public CacheManager defaultCacheManager() {    CaffeineCacheManager cacheManager = new CaffeineCacheManager();    CaffeineSpec spec = CaffeineSpec        .parse("initialCapacity=64,maximumSize=512,expireAfterWrite=300s");    cacheManager.setCacheNames(null);    return cacheManager;  }  @Bean  public CacheManager routeCacheManager() {    CaffeineCacheManager cacheManager = new CaffeineCacheManager();    CaffeineSpec spec = CaffeineSpec        .parse("initialCapacity=512,maximumSize=2048,expireAfterWrite=3000s");    cacheManager.setCacheNames(null);    return cacheManager;  }}
复制代码

以上就简单实现了对 Gateway 的改造,结合业务场景进行具体的性能优化即可,优化后,在路由表较大时(大于 5000 条)能较为明显的提升网关路由性能。

至此修改完成,可以进行下一步测试验证。

测试结果





通过以上图表对比,可以发现,改造后,路由转发性能与路由表大小没有直接关联关系了,性能得到了较大提升。

源码下载

https://gitee.com/eblog/scgw-benchmark-all

https://gitee.com/eblog/scg-dynamic-route

测试记录

直连对照组

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  990.298 ± 219.989  ops/sMyBenchmark.testMethod                       avgt     20    0.002 ±   0.001   s/opMyBenchmark.testMethod                     sample  20205    0.002 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.001             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.011             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.017             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.017             s/opMyBenchmark.testMethod                         ss     20    0.002 ±   0.001   s/op
复制代码

100 条路由(老版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  769.948 ± 112.572  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15364    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.008             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.015             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.015             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

100 条路由(新版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  769.099 ± 110.400  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15541    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.008             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.012             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.012             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

1K 条路由(老版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  759.265 ± 106.047  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15245    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.001             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.007             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.014             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.015             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

1K 条路由(新版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  772.978 ± 102.976  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15101    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.007             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.016             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.016             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

5K 条路由(老版本)

Benchmark                                    Mode   Cnt    Score    Error  UnitsMyBenchmark.testMethod                      thrpt    20  232.624 ±  3.330  ops/sMyBenchmark.testMethod                       avgt    20    0.008 ±  0.001   s/opMyBenchmark.testMethod                     sample  4734    0.009 ±  0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample          0.008            s/opMyBenchmark.testMethod:testMethod·p0.50    sample          0.008            s/opMyBenchmark.testMethod:testMethod·p0.90    sample          0.009            s/opMyBenchmark.testMethod:testMethod·p0.95    sample          0.009            s/opMyBenchmark.testMethod:testMethod·p0.99    sample          0.011            s/opMyBenchmark.testMethod:testMethod·p0.999   sample          0.015            s/opMyBenchmark.testMethod:testMethod·p0.9999  sample          0.016            s/opMyBenchmark.testMethod:testMethod·p1.00    sample          0.016            s/opMyBenchmark.testMethod                         ss    20    0.009 ±  0.001   s/op
复制代码

5K 条路由(新版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  783.074 ± 112.114  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15318    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.001             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.007             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.017             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.017             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

1W 条路由(老版本)

Benchmark                                    Mode   Cnt    Score    Error  UnitsMyBenchmark.testMethod                      thrpt    20  122.122 ±  1.789  ops/sMyBenchmark.testMethod                       avgt    20    0.016 ±  0.001   s/opMyBenchmark.testMethod                     sample  2464    0.016 ±  0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample          0.015            s/opMyBenchmark.testMethod:testMethod·p0.50    sample          0.016            s/opMyBenchmark.testMethod:testMethod·p0.90    sample          0.017            s/opMyBenchmark.testMethod:testMethod·p0.95    sample          0.018            s/opMyBenchmark.testMethod:testMethod·p0.99    sample          0.018            s/opMyBenchmark.testMethod:testMethod·p0.999   sample          0.029            s/opMyBenchmark.testMethod:testMethod·p0.9999  sample          0.030            s/opMyBenchmark.testMethod:testMethod·p1.00    sample          0.030            s/opMyBenchmark.testMethod                         ss    20    0.017 ±  0.001   s/op
复制代码

1W 条路由(新版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  775.200 ± 121.410  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15261    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.001             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.003             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.007             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.014             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.014             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码

10W 条路由(老版本)

Benchmark                                    Mode  Cnt   Score   Error  UnitsMyBenchmark.testMethod                      thrpt   20  12.765 ± 0.338  ops/sMyBenchmark.testMethod                       avgt   20   0.159 ± 0.006   s/opMyBenchmark.testMethod                     sample  260   0.153 ± 0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample        0.147           s/opMyBenchmark.testMethod:testMethod·p0.50    sample        0.152           s/opMyBenchmark.testMethod:testMethod·p0.90    sample        0.157           s/opMyBenchmark.testMethod:testMethod·p0.95    sample        0.159           s/opMyBenchmark.testMethod:testMethod·p0.99    sample        0.163           s/opMyBenchmark.testMethod:testMethod·p0.999   sample        0.167           s/opMyBenchmark.testMethod:testMethod·p0.9999  sample        0.167           s/opMyBenchmark.testMethod:testMethod·p1.00    sample        0.167           s/opMyBenchmark.testMethod                         ss   20   0.155 ± 0.002   s/op
复制代码

10W 条路由(新版本)

Benchmark                                    Mode    Cnt    Score     Error  UnitsMyBenchmark.testMethod                      thrpt     20  774.979 ± 115.501  ops/sMyBenchmark.testMethod                       avgt     20    0.003 ±   0.001   s/opMyBenchmark.testMethod                     sample  15422    0.003 ±   0.001   s/opMyBenchmark.testMethod:testMethod·p0.00    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.50    sample           0.002             s/opMyBenchmark.testMethod:testMethod·p0.90    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.95    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.99    sample           0.004             s/opMyBenchmark.testMethod:testMethod·p0.999   sample           0.005             s/opMyBenchmark.testMethod:testMethod·p0.9999  sample           0.011             s/opMyBenchmark.testMethod:testMethod·p1.00    sample           0.012             s/opMyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op
复制代码


发布于: 2021 年 06 月 17 日阅读数: 30
用户头像

中原银行

关注

打造科技驱动、创新引领的数字化未来银行。 2020.02.06 加入

中原银行是河南省属法人银行,总部位于河南省郑州市。我行坚持“科技立行、科技兴行”,秉承“稳健 创新 进取 高效”理念,发展移动金融、线上金融,提升综合金融服务能力,金融科技应用水平居国内城商行领先地位。

评论

发布
暂无评论
SpringCloud Gateway 路由转发性能优化