zoukankan      html  css  js  c++  java
  • springcloud gateway 的限流重试机制

    前言

    前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其他功能。

    Spring Cloud Gateway中的重试

    我们知道Spring Cloud Gateway中的大多数操作都是使用过滤器模式实现的,该模式是Spring Framework的一种实现GatewayFilter。在这里,我们可以在发送下游请求之前或之后修改传入请求和传出响应。 与我之前关于Spring Cloud Gateway的前两篇文章中描述的示例相同,我们将构建JUnit测试类。它利用TestcontainersMockServer运行模拟暴露的REST端点。 在运行测试之前,我们需要准备一个包含名为Retry过滤器的示例路由。在定义此类类型时,GatewayFilter我们可以设置多个参数。通常,您将使用以下三个:

    1. retries 单个传入请求应尝试的重试次数。该属性的默认值为3

    2. statuses–应该重试的HTTP状态代码列表,使用org.springframework.http.HttpStatus枚举名称表示。

    3. backoff–用于在后续重试尝试之间计算Spring Cloud Gateway超时的策略。默认情况下,此属性是禁用的。

    让我们从最简单的场景开始-使用参数的默认值。在这种情况下,我们只需要GatewayFilter为路线设置一个名称Retry的filter就可以。

    @ClassRule
    public static MockServerContainer mockServer = new MockServerContainer();

    @BeforeClass
    public static void init() {
      // 设置系统参数
       System.setProperty("spring.cloud.gateway.routes[0].id""account-service");
       System.setProperty("spring.cloud.gateway.routes[0].uri""http://192.168.99.100:" + mockServer.getServerPort());
       System.setProperty("spring.cloud.gateway.routes[0].predicates[0]""Path=/account/**");
       System.setProperty("spring.cloud.gateway.routes[0].filters[0]""RewritePath=/account/(?<path>.*), /$\\{path}");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].name""Retry");
      // 使用mockServer容器
       MockServerClient client = new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort());
       // 请求url:/1 生效次数:Times.exactly(3)
       client.when(HttpRequest.request()
          .withPath("/1"), Times.exactly(3))
          .respond(response()
             .withStatusCode(500)
             .withBody("{\"errorCode\":\"5.01\"}")
             .withHeader("Content-Type""application/json"));
      // 请求第4次后走这边配置的/1 
       client.when(HttpRequest.request()
          .withPath("/1"))
          .respond(response()
             .withBody("{\"id\":1,\"number\":\"1234567891\"}")
             .withHeader("Content-Type""application/json"));
    }

    我们的测试方法非常简单。它只是使用Spring FrameworkTestRestTemplate来执行对测试端点的单个调用。

    @Autowired
    TestRestTemplate template;

    @Test
    public void testAccountService() {
       LOGGER.info("Sending /1...");
       ResponseEntity r = template.exchange("/account/{id}", HttpMethod.GET, null, Account.class, 1);
       LOGGER.info("Received: status->{}, payload->{}", r.getStatusCodeValue(), r.getBody());
       Assert.assertEquals(200, r.getStatusCodeValue());
    }

    在运行测试之前,我们要设置一下Spring Cloud Gateway日志的日志记录级别,方便我们可以查看有关重试过程的信息。

    logging.level.org.springframework.cloud.gateway.filter.factory: TRACE

    由于我们未设置任何退避策略,因此立即尝试了后续尝试。如下图所示,默认重试次数为3,并且过滤器尝试重试所有返回500的请求)。

    15:00:49.049 --- [           main] : Started GatewayRetryTest in 3.18 seconds (JVM running for 10.757)
    15:00:49.252 --- [           main] : Sending /1...
    15:00:49.382 --- [ctor-http-nio-2] : Entering retry-filter
    15:00:49.520 --- [ctor-http-nio-3] : setting new iteration in attr 0
    15:00:49.522 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 0, configured retries 3
    15:00:49.523 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:00:49.523 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    15:00:49.571 --- [ctor-http-nio-3] : setting new iteration in attr 1
    15:00:49.571 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 1, configured retries 3
    15:00:49.571 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:00:49.571 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    15:00:49.584 --- [ctor-http-nio-3] : setting new iteration in attr 2
    15:00:49.584 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 2, configured retries 3
    15:00:49.584 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:00:49.584 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    15:00:49.633 --- [ctor-http-nio-3] : setting new iteration in attr 3
    15:00:49.634 --- [ctor-http-nio-3] : exceedsMaxIterations true, iteration 3, configured retries 3
    15:00:49.646 --- [           main] : Received: status->404, payload->null

    java.lang.AssertionError: 
    Expected :200
    Actual   :404
    <Click to see difference>

    现在,让我们设置一些更高级的配置。我们可以更改重试次数,并设置要重试的确切HTTP状态代码,而不是一系列代码。在我们的例子中,重试的状态代码是HTTP 500,因为它是由我们的模拟端点返回的。

    默认触发重试的计算公式为:

    backoff.firstBackoff <= prevBackoff * factor < =backoff.maxBackoff

    • backoff.firstBackoff 开始重试的时间
    • backoff.maxBackoff 最大重试的时间
    • backoff.factor 重试因子
    • basedOnPreviousValue
      • 当设置为false时候 计算重试值变成: firstBackoff * (factor ^ n)
      • 当设置为true时候 计算重试值变成: prevBackoff * factor
    @ClassRule
    public static MockServerContainer mockServer = new MockServerContainer();

    @BeforeClass
    public static void init() {
       System.setProperty("spring.cloud.gateway.routes[0].id""account-service");
       System.setProperty("spring.cloud.gateway.routes[0].uri""http://192.168.99.100:" + mockServer.getServerPort());
       System.setProperty("spring.cloud.gateway.routes[0].predicates[0]""Path=/account/**");
       System.setProperty("spring.cloud.gateway.routes[0].filters[0]""RewritePath=/account/(?<path>.*), /$\\{path}");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].name""Retry");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.retries""10");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.statuses""INTERNAL_SERVER_ERROR");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.backoff.firstBackoff""50ms");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.backoff.maxBackoff""500ms");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.backoff.factor""2");
       System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.backoff.basedOnPreviousValue""true");
       MockServerClient client = new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort());
       client.when(HttpRequest.request()
          .withPath("/1"), Times.exactly(3))
          .respond(response()
             .withStatusCode(500)
             .withBody("{\"errorCode\":\"5.01\"}")
             .withHeader("Content-Type""application/json"));
       client.when(HttpRequest.request()
          .withPath("/1"))
          .respond(response()
             .withBody("{\"id\":1,\"number\":\"1234567891\"}")
             .withHeader("Content-Type""application/json"));
       // OTHER RULES
    }

    如果使用新配置再运行一次相同的测试,则日志看起来会有些不同。我在下面的图片中强调了最重要的区别。如您所见,仅HTTP 500状态的当前重试次数为10。设置重试策略后,第一次重试尝试在50毫秒后执行,第二次重试在100毫秒后进行,第三次重试在200毫秒后进行,依此类推。(日志记录时间会有一点延迟,大家可以改变factor进行验证)

    15:24:01.133 --- [           main] : Sending /1...
    15:24:01.288 --- [ctor-http-nio-2] : Entering retry-filter
    ## 第一次请求时间 419
    15:24:01.419 --- [ctor-http-nio-3] : setting new iteration in attr 0
    15:24:01.421 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 0, configured retries 3
    15:24:01.422 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:24:01.423 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    ## 第一次重试时间 494  重试时间大概在 494 -419 = 75  
    15:24:01.494 --- [ctor-http-nio-3] : setting new iteration in attr 1
    15:24:01.494 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 1, configured retries 3
    15:24:01.494 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:24:01.494 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    ## 第二次重试时间 623  重试时间大概在 623 -494 = 129 
    15:24:01.623 --- [ctor-http-nio-3] : setting new iteration in attr 2
    15:24:01.623 --- [ctor-http-nio-3] : exceedsMaxIterations false, iteration 2, configured retries 3
    15:24:01.623 --- [ctor-http-nio-3] : retryableStatusCode: true, statusCode 500 INTERNAL_SERVER_ERROR, configured statuses [500 INTERNAL_SERVER_ERROR], configured series [SERVER_ERROR]
    15:24:01.623 --- [ctor-http-nio-3] : retryableMethod: true, httpMethod GET, configured methods [GET]
    ## 第二次重试时间 623  重试时间大概在 852 -623 = 230 
    15:24:01.852 --- [ctor-http-nio-3] : setting new iteration in attr 3
    15:24:01.852 --- [ctor-http-nio-3] : exceedsMaxIterations true, iteration 3, configured retries 3
    15:24:01.878 --- [           main] : Received: status->200, payload->Account(id=1, number=1234567891)

    Spring Cloud Gateway中的超时

    超时是请求路由的另一个重要方面。使用Spring Cloud Gateway,我们可以轻松设置全局读取和连接超时。另外,我们也可以为每个路线分别定义它们。让我们在测试路由定义中添加以下属性全局超时设置为100ms。现在,我们的测试路由包含一个测试Retry过滤器,其新添加的全局读取超时为100ms。

    System.setProperty("spring.cloud.gateway.httpclient.response-timeout""100ms");

    另外,我们可以设置每条路由的超时时间。如果我们在这里更喜欢这样的解决方案,则应该在示例测试中添加一行。

    System.setProperty("spring.cloud.gateway.routes[1].metadata.response-timeout""100");

    然后,我们定义一个请求路径为/2具有200ms延迟的另一个测试端点。我们当前的测试方法与前一种方法非常相似,不同之处在于,我们期望使用HTTP 504作为结果。

    @Test
    public void testAccountServiceFail() {
       LOGGER.info("Sending /2...");
       ResponseEntity<Account> r = template.exchange("/account/{id}", HttpMethod.GET, null, Account.class, 2);
       LOGGER.info("Received: status->{}, payload->{}", r.getStatusCodeValue(), r.getBody());
       Assert.assertEquals(504, r.getStatusCodeValue());
    }

    让我们进行测试。结果在下面的图片中可见。日志中请求在几次失败重试后,由于设置最大的重试时间为500ms。Retry过滤器会打印出IOException和TimeoutException。

    15:41:15.814 --- [           main] : Sending /2...
    15:41:15.940 --- [ctor-http-nio-2] : Entering retry-filter
    15:41:16.077 --- [     parallel-1] : setting new iteration in attr 0
    15:41:16.078 --- [     parallel-1] : exceedsMaxIterations false, iteration 0, configured retries 3
    15:41:16.079 --- [     parallel-1] : exception or its cause is retryable org.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException}, configured exceptions [class java.io.IOException, class org.springframework.cloud.gateway.support.TimeoutException]
    15:41:16.239 --- [     parallel-3] : setting new iteration in attr 1
    15:41:16.240 --- [     parallel-3] : exceedsMaxIterations false, iteration 1, configured retries 3
    15:41:16.240 --- [     parallel-3] : exception or its cause is retryable org.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException}, configured exceptions [class java.io.IOException, class org.springframework.cloud.gateway.support.TimeoutException]
    15:41:16.500 --- [     parallel-5] : setting new iteration in attr 2
    15:41:16.500 --- [     parallel-5] : exceedsMaxIterations false, iteration 2, configured retries 3
    15:41:16.500 --- [     parallel-5] : exception or its cause is retryable org.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException}, configured exceptions [class java.io.IOException, class org.springframework.cloud.gateway.support.TimeoutException]
    15:41:17.058 --- [     parallel-7] : setting new iteration in attr 3
    15:41:17.059 --- [     parallel-7] : exceedsMaxIterations true, iteration 3, configured retries 3
    15:41:17.128 --- [           main] : Received: status->504, payload->Account(id=null, number=null)

    END

    下篇给大家介绍Spring Cloud Gateway的限流

    欢迎关注公众号! 公众号回复:入群 ,扫码加入我们交流群! 扫码关注公众号获取更多学习资料

  • 相关阅读:
    生成函数
    FFT【快速傅里叶变换】FWT【快速沃尔什变换】
    牛客网多校赛第9场 E-Music Game【概率期望】【逆元】
    hdu6393Traffic Network in Numazu【树状数组】【LCA】
    hdu 6395Sequence【矩阵快速幂】【分块】
    hdu6390GuGuFishtion【数论】
    欧拉函数和莫比乌斯函数
    hdu4614 Vases and Flowers【线段树】【二分】
    hdu4578 Transformation【线段树】
    hdu3974 Assign the task【线段树】
  • 原文地址:https://www.cnblogs.com/1ssqq1lxr/p/14680660.html
Copyright © 2011-2022 走看看