ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringCloud Gateway 路由转发性能优化

2021-06-16 12:32:28  阅读:499  来源: 互联网

标签:MyBenchmark 20 testMethod SpringCloud sample 0.001 Gateway 路由 op


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

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

查阅源码

通过阅读

RoutePredicateHandlerMapping.java

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

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
  return this.routeLocator.getRoutes()
      // individually filter routes so that filterWhen error delaying is not a
      // problem
      .concatMap(route -> Mono.just(route).filterWhen(r -> {
        // add the current route we are testing
        exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
        return r.getPredicate().apply(exchange);
      })
          // instead of immediately stopping main flux due to error, log and
          // swallow it
          .doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
          .onErrorResume(e -> Mono.empty()))
      // .defaultIfEmpty() put a static Route not found
      // or .switchIfEmpty()
      // .switchIfEmpty(Mono.<Route>empty().log("noroute"))
      .next()
      // TODO: error handling
      .map(route -> {
        if (logger.isDebugEnabled()) {
          logger.debug("Route matched: " + route.getId());
        }
        validateRoute(route, exchange);
        return route;
      });
  /*
   * TODO: trace logging if (logger.isTraceEnabled()) {
   * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
   */
}

 

如果对源码简单做一下修改,比如,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()
      // individually filter routes so that filterWhen error delaying is not a
      // problem
      .concatMap(route -> Mono.just(route).filterWhen(r -> {
        // add the current route we are testing
        exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
        return r.getPredicate().apply(exchange);
      })
          // instead of immediately stopping main flux due to error, log and
          // swallow it
          .doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
          .onErrorResume(e -> Mono.empty()))
      // .defaultIfEmpty() put a static Route not found
      // or .switchIfEmpty()
      // .switchIfEmpty(Mono.<Route>empty().log("noroute"))
      .next()
      // TODO: error handling
      .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 也在其中,代码入下:

@Bean
public 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;
  }
}

 

缓存管理配置

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

备用改造方案(不推荐该方式)

查阅GatewayAutoConfiguration 源码,类定义头部有一个装配条件:

@Configuration(proxyBeanMethods = false)
// 该条件可以作为装配点
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayReactiveLoadBalancerClientAutoConfiguration.class,
		GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration

可以在配置文件中配置

spring.cloud.gateway.enabled=false

然后将GatewayAutoConfiguration 拷贝到我们自己的工程中,去掉装配条件

// @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

同样的,我们需要找到所有依赖此条件装配的类,进行上述操作

 

 

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

测试结果

 

 

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

源码下载

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

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

测试记录

直连对照组

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

 

 

原文链接:【https://xie.infoq.cn/article/bebacc42bad0712638ba3231e】。

标签:MyBenchmark,20,testMethod,SpringCloud,sample,0.001,Gateway,路由,op
来源: https://blog.csdn.net/FrankWong1989/article/details/117954307

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有