前言

前面给大家介绍了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的限流

欢迎关注公众号!
公众号回复:入群 ,扫码加入我们交流群!

深入了解springcloud gateway 的限流重试机制的更多相关文章

  1. Gateway的限流重试机制详解

    前言 想要源码地址的可以加上此微信:Lemon877164954  前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其 ...

  2. spring boot gateway自定义限流

    参考:https://blog.csdn.net/ErickPang/article/details/84680132 采用自带默认网关请参照微服务架构spring cloud - gateway网关 ...

  3. SpringCloud zuul 网关限流分析

    最近项目中 spring cloud zuul 运用到限流功能,打算配置一下就直接使用,不过在压测与调优过程中遇到一些没有预测到的问题,附上排查与解析结果 yml.pom配置 强烈推荐,按最新gith ...

  4. spring cloud gateway 之限流篇

    转载请标明出处: https://www.fangzhipeng.com 本文出自方志朋的博客 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方 ...

  5. 微服务架构spring cloud - gateway网关限流

    1.算法 在高并发的应用中,限流是一个绕不开的话题.限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击. 一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池.线程池). ...

  6. Spring Cloud Gateway 网关限流

    Spring Cloud Gateway 限流 一.背景 二.实现功能 三.网关层限流 1.使用默认的redis来限流 1.引入jar包 2.编写配置文件 3.网关正常响应 4.网关限流响应 2.自定 ...

  7. SpringCloud Alibaba Sentinel 限流详解

    点赞再看,养成习惯,微信搜索[牧小农]关注我获取更多资讯,风里雨里,小农等你,很高兴能够成为你的朋友. 项目源码地址:公众号回复 sentinel,即可免费获取源码 熔断规则 在上一篇文章中我们讲解了 ...

  8. SpringCloud Gateway高阶之Sentinel限流、熔断

    前言 为什么需要服务熔断和降级?微服务是当前业界的一大趋势,原理就是将单一职责的功能模块独立化为子服务,降低服务间的耦合,服务间互相调用.但是这样也会出现一些问题: 上图中大量微服务互相调用,存在大量 ...

  9. Spring Cloud 微服务五:Spring cloud gateway限流

    前言:在互联网应用中,特别是电商,高并发的场景非常多,比如:秒杀.抢购.双11等,在开始时间点会使流量爆发式地涌入,如果对网络流量不加控制很有可能造成后台实例资源耗尽.限流是指通过指定的策略削减流量, ...

随机推荐

  1. better-scroll使用参考

    ************better-scroll是基于父元素固定高度,溢出才滚动的,所以父元素务必定高,否则无法滚动***************************************** ...

  2. BGV上线17小时最高888.88美金,投资最高回报率近+1778倍, 带动NGK内存暴涨

    至12月3日BGV币上线A网交易所DeFi板块以来,BGV价值飙升长.,据非小号的数据显示,BGV币价是718美元(东八区时间2020年12月4日早上九点四十),相较昨日涨幅达70.14%,以718美 ...

  3. java线程池趣味事:这不是线程池

    要想写出高性能高并发的应用,自然有许多关键,如io,算法,异步,语言特性,操作系统特性,队列,内存,cpu,分布式,网络,数据结构,高性能组件. 胡说一通先. 回到主题,线程池.如果说多线程是提高系统 ...

  4. Elasticsearch 及其套件的安装上手

    前言 本文主要讲解Elasticsearch及其套件Kibana.Logstash的安装及启动,还讲解如何导入数据用于后续的实验. 说明:Elasticsearch是基于Java开发的,所以如果是下载 ...

  5. idea分布式创建子模块后不能创建java文件

    问题描述:多模块情况下,创建java文件,找不到java类,如下图,即使手动创建,在里面编写内容也没有任何反应. 解决方案:右键将文件标记为Sources Root便可以了,如果想要标记为资源文件的话 ...

  6. 边缘计算k8s集群之SuperEdge

    什么是边缘计算? 边缘计算,是指在靠近物或数据源头的一侧,采用网络.计算.存储.应用核心能力为一体的开放平台,就近提供最近端服务.其应用程序在边缘侧发起,产生更快的网络服务响应,满足行业在实时业务.应 ...

  7. 一个 java 文件的执行过程详解

    平时我们都使用 idea.eclipse 等软件来编写代码,在编写完之后直接点击运行就可以启动程序了,那么这个过程是怎么样的? 总体过程 我们编写的 java 文件在由编译器编译后会生成对应的 cla ...

  8. day1_安装及建立数据库和表

    #第一份数据库及表create database library; use library; create table book( id int primary key, book_name char ...

  9. ElasticSearch(ES)使用Nested结构存储KV及聚合查询

    自建博客地址:https://www.bytelife.net,欢迎访问! 本文为博客同步发表文章,为了更好的阅读体验,建议您移步至我的博客 本文作者: Jeffrey 本文链接: https://w ...

  10. 漏洞复现-CVE-2015-1427-Groovy远程代码执行

          0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 Elasticsearch 1.3.0-1.3. ...