Bootstrap

SpringCloud Gateway 路由转发性能优化

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

第一篇:

第二篇:

第三篇:

第四篇:

第五篇:SpringCloud Gateway 过滤器。

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

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

源码解读

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

protected Mono lookupRoute(ServerWebExchange exchange) {...}

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

public static final String MOCK_PATCH = "/mock/**";
private Map hashCache = new ConcurrentHashMap<>(1024);
​
protected Mono 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 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;
      });
}

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

@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
    RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
  return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
}

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

我们只能采取以下步骤:

改造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 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条)能较为明显的提升网关路由性能。

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

测试结果

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

源码下载

测试记录

直连对照组

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

100条路由(老版本)

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

100条路由(新版本)

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

1K条路由(老版本)

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

1K条路由(新版本)

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

5K条路由(老版本)

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

5K条路由(新版本)

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

1W条路由(老版本)

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

1W条路由(新版本)

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

10W条路由(老版本)

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

10W条路由(新版本)

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