前言

想要源码地址的可以加上此微信:Lemon877164954 

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

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

Gateway的限流重试机制详解的更多相关文章

  1. 深入了解springcloud gateway 的限流重试机制

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

  2. Java之Retry重试机制详解

    应用中需要实现一个功能: 需要将数据上传到远程存储服务,同时在返回处理成功情况下做其他操作.这个功能不复杂,分为两个步骤:第一步调用远程的Rest服务上传数据后对返回的结果进行处理:第二步拿到第一步结 ...

  3. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

  4. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  5. java异常处理机制详解

    java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我 ...

  6. Redis主从复制机制详解

    Redis主从复制机制详解 Redis有两种不同的持久化方式,Redis服务器通过持久化,把Redis内存中持久化到硬盘当中,当Redis宕机时,我们重启Redis服务器时,可以由RDB文件或AOF文 ...

  7. java 深拷贝与浅拷贝机制详解

    概要: 在Java中,拷贝分为深拷贝和浅拷贝两种.java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝. (一 ...

  8. 【转载】Android应用AsyncTask处理机制详解及源码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...

  9. 从mixin到new和prototype:Javascript原型机制详解

    从mixin到new和prototype:Javascript原型机制详解   这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...

随机推荐

  1. NGK钱包真的安全吗?

    对于数字资产持有者而言,资产的安全永远是首要的,因而数字钱包的安全性显得尤为重要.数字钱包分为冷钱包和热钱包两种.热钱包叫做在线钱包,而冷钱包被称为离线钱包,也叫硬件钱包.数字钱包一旦被盗,被追回的概 ...

  2. 基于NGK发行的稳定币USDN如何撼动市场?

    近日,基于NGK发行的稳定币USDN在各大社区的热度越来越高,很多人都说USDN将会撼动市场,那么USDN究竟有怎样的优势,能引起这么大的轰动呢?今天我们就一起来分析一下USDN. USDN是基于公链 ...

  3. fork后子进程与父进程的关系

    共享代码空间,各自独立数据空间,子进程初始化数据是父进程的复制. 资料参考: https://blog.csdn.net/u013851082/article/details/76902046

  4. Python数据结构与算法_回文数(03)

    判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121输出: true 示例 2: 输入: -121输出: false解释: 从左向右读, ...

  5. 自己写的一个抢票加速的Python小程序源码分享-----纯属娱乐

    最近这段时间频频看到微信群里发什么 抢票加速,智行.携程.飞猪.美团,对于我这能坐客车就不坐火车的人来说,无所谓靠谱不靠谱 突发奇想的整理了下整个抢票加速的逻辑,写了这个小程序,代码很low,拒绝批评 ...

  6. 后端程序员之路 10、gbdt(Gradient Boosting Decision Tree)

    1.GbdtModelGNode,含fea_idx.val.left.right.missing(指向left或right之一,本身不分配空间)load,从model文件加载模型,xgboost输出的 ...

  7. 《C++ Primer》笔记 第7章 类

    成员函数的声明必须在类的内部,它的定义则既可以在类的内部也可以在类的外部.作为接口组成部分的非成员函数,它们的定义和声明都在类的外部. 定义在类内部的函数是隐式的inline函数. 成员函数通过一个名 ...

  8. 不使用map和set实现LRU——那用List?

    遇到一道面试题,不使用map和set实现LRU,要求get的时间复杂度为O(logn),put的时间复杂度不超过O(n).想到了用ArrayList来实现,保存有序的key.然而牵涉add节点,在保证 ...

  9. TKE 容器网络中的 ARP Overflow 问题探究及其解决之道

    作者朱瑜坚,腾讯云后台开发工程师,熟悉 CNI 容器网络相关技术,负责腾讯云 TKE 的容器网络的构建和相关网络组件的开发维护工作,作为主力开发实现了 TKE 下一代容器网络方案. 1. 问题背景 1 ...

  10. MySQL使用入门--初识数据库

    MySQL使用入门 数据库概述 数据库是存放数据的仓库.在应用的开发中总是离不开数据的查询.处理.存储,例如图书管理系统就需要操纵和存储大量的数据.没有数据库之前我们使用文件存储数据,但是文件存储有很 ...