我是靠谱客的博主 贤惠鼠标,最近开发中收集的这篇文章主要介绍SpringCloud Gateway 路由数量对性能的影响研究,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

背景描述

近期在公司建设API治理平台过程中,以SpringCloud Gateway为基础,构建了一个API的Mock服务,以API的URI作为路由,根据服务端存储的API DSL,验证请求信息,生成并返回Mock报文。

 

SpringCloud Gateway具备很好的 动态路由 支持功能,可以在API DSL创建的同时,创建一条Mock路由,这样API DSL创建后,开发人员就可以使用Mock服务进行开发调试工作。

 

但作为一个企业级应用,所管辖的API的数量众多,SpringCloud Gateway在路由表急剧膨胀后的性能如何?目前没有查阅到明确说明的资料。翻阅其源码发现,断言命中其实是进行的遍历,这样路由表膨胀时,路由性能存疑,需要进行验证,根据验证结果制定近一步优化方案。

验证方法

保持其他所有变量不变,仅调整路由表大小,使用JMH工具进行基准测试,得出路由表膨胀与路由性能的关系。

需要编写三个服务:

  1. 路由网关

  2. 挡板服务(路由转发的upstream)

  3. JMH测试端

 

配置信息

  CPU Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz

  基准速度:  3.60 GHz
  插槽:  1
  内核:  8
  逻辑处理器:  8
  虚拟化:  已启用
  L1 缓存:  512 KB
  L2 缓存:  2.0 MB
  L3 缓存:  12.0 MB

  测试期间CPU利用率约  50%

  SpringCloud Gateway 3.0.3, Java 1.8.0_251

 

测试过程需控制变量,所以CPU负债不能过高,下图是我执行测试时的CPU负载情况

 

路由定义示例

 

{
    "_id": {
        "$oid": "60c4f055588bca06132e44cb"
    },
    "predicates": [{
        "name": "Path",
        "args": {
            "_genkey_0": "/1999"
        }
    }],
    "filters": [{
        "name": "AddRequestHeader",
        "args": {
            "_genkey_0": "x-benchmark-routeId",
            "_genkey_1": "60c4f055588bca06132e44cb"
        }
    }, {
        "name": "PrefixPath",
        "args": {
            "_genkey_0": "/ok"
        }
    }],
    "uri": "http://localhost:9999",
    "metadata": {},
    "order": 1,
    "_class": "com.example.scgw.benchmark.route.MongoRouteDefinition"
}

测试结论

 

 

 

 

见上图,测试结果无论系统的吞吐量,还是响应时间指标,都随着路由表的膨胀变差,当路由表膨胀到10W级别时,服务基本不可用了!

 

这一结论印证了我的猜想。

优化思路一

保持路由网关的通用性(SpringCloud Gateway设计了很多路由断言手段,包括基于path、method、header、parameter等等),采用两级(多级)路由机制见下图:

 

 

 

优化思路二

Mock服务是一个专有场景,仅根据Path和指定Header进行转发,可以修改SpringCloud Gateway源码,提供一个根据Path+指定Header查找路由的HashMap,这样进行路由断言时就不需进行路由表遍历。

 

个人倾向于按照思路二进行优化,优化过程见SpringCloud Gateway 路由转发性能优化。

测试相关代码

路由网关

pom.xml

 

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

 

MongoRouteDefinition.java

 

@Document
public class MongoRouteDefinition extends RouteDefinition {

  @Id
  private String id;

  @Override
  public String getId() {
    return this.id;
  }

  @Override
  public void setId(String id) {
    this.id = id;
  }

  public static MongoRouteDefinition from(RouteDefinition route) {
    MongoRouteDefinition newRoute = new MongoRouteDefinition();
    BeanUtils.copyProperties(route, newRoute);
    return newRoute;
  }
}

 

MongoRouteRepository.java

 

public interface MongoRouteRepository extends
    ReactiveMongoRepository<MongoRouteDefinition, String> {

}

 

MongoRouteDefinitionRepository.java

 

@Component
public class MongoRouteDefinitionRepository implements RouteDefinitionRepository {

  private final MongoRouteRepository mongoRouteRepository;

  public MongoRouteDefinitionRepository(
      MongoRouteRepository mongoRouteRepository) {
    this.mongoRouteRepository = mongoRouteRepository;
  }

  @Override
  public Flux<RouteDefinition> getRouteDefinitions() {
    return mongoRouteRepository.findAll().map(r -> r);
  }

  @Override
  public Mono<Void> save(Mono<RouteDefinition> route) {
    return route.flatMap(
        r -> mongoRouteRepository.save(MongoRouteDefinition.from(r))
            .and(Mono.empty())
    );
  }

  @Override
  public Mono<Void> delete(Mono<String> routeId) {
    return routeId.flatMap(mongoRouteRepository::deleteById);
  }

  public Mono<MongoRouteDefinition> save(MongoRouteDefinition route) {
    return mongoRouteRepository.save(route);
  }
  
  public Mono<Void> delete(String routeId) {
    return mongoRouteRepository.deleteById(routeId);
  }

  public Flux<MongoRouteDefinition> saveAll(Flux<RouteDefinition> routes) {
    return mongoRouteRepository
        .saveAll(routes.map(MongoRouteDefinition::from));
  }

  public Flux<MongoRouteDefinition> saveAll(List<MongoRouteDefinition> routes) {
    return mongoRouteRepository.saveAll(routes);
  }

  public Mono<Void> deleteAll() {
    return mongoRouteRepository.deleteAll();
  }
}

 

MongoRouteDefinitionRepositoryTest.java

 

@SpringBootTest
class MongoRouteDefinitionRepositoryTest {

  public static final String UP_STREAM = "http://localhost:9999";
  @Autowired
  MongoRouteDefinitionRepository mongoRepository;

  @Test
  void save() {
    Mono<MongoRouteDefinition> route = mongoRepository.save(newRoute(-1));

    StepVerifier.create(route)
        .expectNextMatches(r -> r.getId() != null)
        .expectComplete()
        .verify();
  }

  @Test
  void saveAll() {
    this.deleteAll();
    int cycle = 100;
    while (cycle-- > 0) {
      int count = 100;
      List<MongoRouteDefinition> routes = new ArrayList<>(count);
      while (count-- > 0) {
        routes.add(newRoute(cycle * 100 + count));
      }
      mongoRepository.saveAll(routes).blockLast();
    }
  }

  @Test
  void deleteAll() {
    mongoRepository.deleteAll().block();
  }

  private MongoRouteDefinition newRoute(int path) {
    MongoRouteDefinition route = new MongoRouteDefinition();
    route.setId(ObjectId.get().toHexString());
    PredicateDefinition predicate = new PredicateDefinition(
        "Path=/mock/" + path);
    route.setPredicates(Collections.singletonList(predicate));
    List<FilterDefinition> filters = new LinkedList<>();
    filters.add(new FilterDefinition("AddRequestHeader=x-benchmark-routeId," + route.getId()));
    filters.add(new FilterDefinition("PrefixPath=/ok"));
    route.setFilters(filters);
    route.setOrder(1);
    route.setUri(URI.create(UP_STREAM));
    return route;
  }

 

application.properties

 

server.port=9999

挡板服务

pom.xml

 

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

 

java

 

public class MockApplication {

  public static void main(String[] args) {
    SpringApplication.run(MockApplication.class, args);
  }
  
  @Bean
  RouterFunction<ServerResponse> defaultRouter() {
    return route(path("/ok/**"), this::success);
  }

  Mono<ServerResponse> success(ServerRequest request) {
    String routeId = request.headers().firstHeader("x-benchmark-routeId");
    return ok()
        .header("x-mock-server-routeId", routeId)
        .contentType(MediaType.TEXT_PLAIN)
        .body(Mono.just(String.valueOf(routeId)), String.class);
  }
}

 

application.properties

 

server.port=9999

JMH 代码

创建工程

 

mvn archetype:generate 
          -DinteractiveMode=false 
          -DarchetypeGroupId=org.openjdk.jmh 
          -DarchetypeArtifactId=jmh-java-benchmark-archetype 
          -DgroupId=com.example 
          -DartifactId=jmh 
          -Dversion=1.32

 

修改POM文件,添加http客户端依赖

 

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>4.9.1</version>
</dependency>

 

编写测试案例

 

public class MyBenchmark {

  @Benchmark
  @Threads(2)
  @Fork(2)
  @BenchmarkMode(Mode.All)
  @Warmup(iterations = 1, time = 3)
  @Measurement(iterations = 10, time = 1)
  @Timeout(time = 300)
  public void testMethod() {
    // 根据路由总数设置随机路由path
    int path = (int) (Math.random() * 100000);
    testRoute(path);
  }

  /**
   * 待测试方法
   */
  public void testRoute(int path) {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
        .url("http://127.0.0.1:8888/mock/" + path)
        .build();
    Call call = client.newCall(request);
    try {
      Response response = call.execute();
      assert response.body() != null;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

执行性能基准测试

 

方式1,IDEA的JMH插件JMH plugin安装后,直接在IDEA里运行;

 

方式2,mvn 打包,执行jar包

 

mvn package
java -jar target/benchmarks.jar

 

收集测试结果

 

测试结果数据见下文,共6组。

测试数据

对照组(直连)

Benchmark                                    Mode    Cnt    Score     Error  Units
MyBenchmark.testMethod                      thrpt     20  899.152 ± 137.062  ops/s
MyBenchmark.testMethod                       avgt     20    0.002 ±   0.001   s/op
MyBenchmark.testMethod                     sample  17647    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.003             s/op
MyBenchmark.testMethod:testMethod·p0.999   sample           0.012             s/op
MyBenchmark.testMethod:testMethod·p0.9999  sample           0.018             s/op
MyBenchmark.testMethod:testMethod·p1.00    sample           0.018             s/op
MyBenchmark.testMethod                         ss     20    0.003 ±   0.001   s/op

100条路由

Benchmark                                    Mode    Cnt    Score    Error  Units
MyBenchmark.testMethod                      thrpt     20  713.931 ± 97.302  ops/s
MyBenchmark.testMethod                       avgt     20    0.003 ±  0.001   s/op
MyBenchmark.testMethod                     sample  14090    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.004            s/op
MyBenchmark.testMethod:testMethod·p0.95    sample           0.004            s/op
MyBenchmark.testMethod:testMethod·p0.99    sample           0.005            s/op
MyBenchmark.testMethod:testMethod·p0.999   sample           0.008            s/op
MyBenchmark.testMethod:testMethod·p0.9999  sample           0.011            s/op
MyBenchmark.testMethod:testMethod·p1.00    sample           0.011            s/op
MyBenchmark.testMethod                         ss     20    0.003 ±  0.001   s/op

1000条路由

Benchmark                                    Mode    Cnt    Score    Error  Units
MyBenchmark.testMethod                      thrpt     20  630.823 ± 41.301  ops/s
MyBenchmark.testMethod                       avgt     20    0.003 ±  0.001   s/op
MyBenchmark.testMethod                     sample  12617    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.004            s/op
MyBenchmark.testMethod:testMethod·p0.95    sample           0.004            s/op
MyBenchmark.testMethod:testMethod·p0.99    sample           0.005            s/op
MyBenchmark.testMethod:testMethod·p0.999   sample           0.008            s/op
MyBenchmark.testMethod:testMethod·p0.9999  sample           0.009            s/op
MyBenchmark.testMethod:testMethod·p1.00    sample           0.009            s/op
MyBenchmark.testMethod                         ss     20    0.004 ±  0.001   s/op

5000条路由

Benchmark                                    Mode   Cnt    Score    Error  Units
MyBenchmark.testMethod                      thrpt    20  248.353 ±  4.634  ops/s
MyBenchmark.testMethod                       avgt    20    0.008 ±  0.001   s/op
MyBenchmark.testMethod                     sample  5007    0.008 ±  0.001   s/op
MyBenchmark.testMethod:testMethod·p0.00    sample          0.002            s/op
MyBenchmark.testMethod:testMethod·p0.50    sample          0.009            s/op
MyBenchmark.testMethod:testMethod·p0.90    sample          0.009            s/op
MyBenchmark.testMethod:testMethod·p0.95    sample          0.011            s/op
MyBenchmark.testMethod:testMethod·p0.99    sample          0.014            s/op
MyBenchmark.testMethod:testMethod·p0.999   sample          0.017            s/op
MyBenchmark.testMethod:testMethod·p0.9999  sample          0.019            s/op
MyBenchmark.testMethod:testMethod·p1.00    sample          0.019            s/op
MyBenchmark.testMethod                         ss    20    0.009 ±  0.001   s/op

1w条路由

Benchmark                                    Mode   Cnt    Score    Error  Units
MyBenchmark.testMethod                      thrpt    20  131.585 ±  1.799  ops/s
MyBenchmark.testMethod                       avgt    20    0.015 ±  0.001   s/op
MyBenchmark.testMethod                     sample  2671    0.015 ±  0.001   s/op
MyBenchmark.testMethod:testMethod·p0.00    sample          0.002            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.028            s/op
MyBenchmark.testMethod:testMethod·p0.999   sample          0.030            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.016 ±  0.001   s/op

10W条路由

Benchmark                                    Mode  Cnt   Score   Error  Units
MyBenchmark.testMethod                      thrpt   20  13.880 ± 0.364  ops/s
MyBenchmark.testMethod                       avgt   20   0.141 ± 0.002   s/op
MyBenchmark.testMethod                     sample  300   0.141 ± 0.002   s/op
MyBenchmark.testMethod:testMethod·p0.00    sample        0.005           s/op
MyBenchmark.testMethod:testMethod·p0.50    sample        0.141           s/op
MyBenchmark.testMethod:testMethod·p0.90    sample        0.142           s/op
MyBenchmark.testMethod:testMethod·p0.95    sample        0.143           s/op
MyBenchmark.testMethod:testMethod·p0.99    sample        0.144           s/op
MyBenchmark.testMethod:testMethod·p0.999   sample        0.270           s/op
MyBenchmark.testMethod:testMethod·p0.9999  sample        0.270           s/op
MyBenchmark.testMethod:testMethod·p1.00    sample        0.270           s/op
MyBenchmark.testMethod                         ss   20   0.141 ± 0.001   s/op

 

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

最后

以上就是贤惠鼠标为你收集整理的SpringCloud Gateway 路由数量对性能的影响研究的全部内容,希望文章能够帮你解决SpringCloud Gateway 路由数量对性能的影响研究所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部