前言

前面给大家介绍了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就可以。

  1. @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来执行对测试端点的单个调用。

  1. @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日志的日志记录级别,方便我们可以查看有关重试过程的信息。

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

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

  1. 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
  1. @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进行验证)

  1. 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。

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

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

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

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

  1. @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。

  1. 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. NGK项目八大板块是什么?

    公链项目生态各板块中,应用生态繁荣与实体经济联系作为密切,也被看做公链平台追求的终极目标,NGK公链以实体企业粘合客户,致力于重塑金融体系,构建全球区块链生态. NGK让经济权益上链发行,目前已有八大 ...

  2. 「NGK每日快讯」2021.1.4日NGK第62期官方快讯!

  3. 精密进近OAS面的绘制与评估

    一.定义:精密进近OAS面(Obstacle Assessment Surface 障碍物评价面)是在精密进近程序中,用来对障碍物进行评估,找出影响运行标准的控制障碍物的一种计算方法. 二.构成 OA ...

  4. 用OkHttpGo和FastJson获取OneNET云平台数据(解析嵌套数组)

    JSON数据格式有两种,一种是 { } 大括号表示的JSON对象,一种是 [ ] 中括号表示的JSON数组.从OneNET获取到的数组是这样的,并用Json解析网址查看https://jsonform ...

  5. Vue学习笔记-rest_framework_jwt 学习

    一  使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7  (Windows x86- ...

  6. 关于GitHub 搭建 Hexo 总结

    问题描述 在更新上传了一篇新博客后,本地运行http://localhost:4001正常,而连接到Github仓库便爆出404错误. 更新博客后,依次执行: 1 hexo clean 2 hexo ...

  7. 8.Vue组件三---slot插槽

    主要内容:  1. 什么是插槽 2. 组件的插槽 3. 插槽的使用方法 4. 插槽的具名 5. 变量的作用域 6. slot的作用域 一. 什么是插槽呢? 1. 生活中的插槽有哪些呢? usb插槽, ...

  8. 保姆级别学生党安装Clion IDE(面向华师同学)

    保姆级别学生党安装Clion IDE(面向华师同学) 界面UI 废话不多说,直接上图 具备功能 UI美观 (下面会介绍) 基础的代码编写能力 大容量的IDE插件 (下面会介绍) 代码补全,以及搭配Ki ...

  9. 01.从0实现一个JVM语言之架构总览

    00.一个JVM语言的诞生过程 文章集合以及项目展望 源码github地址 这一篇将是架构总览, 将自顶向下地叙述自制编译器的要素; 文章目录 01.从0实现一个JVM语言之架构总览 架构总览目前完成 ...

  10. 通过kubeadm快速部署K8S集群

    kubeadm是官方社区推出的一个用于快速部署kubernetes集群的工具. 这个工具能通过两条指令完成一个kubernetes集群的部署: # 创建一个 Master 节点 $ kubeadm i ...