前言
前面给大家介绍了Spring Cloud Gateway的入门教程,这篇给大家探讨下Spring Cloud Gateway的一些其他功能。
Spring Cloud Gateway中的重试
我们知道Spring Cloud Gateway中的大多数操作都是使用过滤器模式实现的,该模式是Spring Framework的一种实现GatewayFilter。在这里,我们可以在发送下游请求之前或之后修改传入请求和传出响应。与我之前关于Spring Cloud Gateway的前两篇文章中描述的示例相同,我们将构建JUnit测试类。它利用TestcontainersMockServer运行模拟暴露的REST端点。在运行测试之前,我们需要准备一个包含名为Retry过滤器的示例路由。在定义此类类型时,GatewayFilter我们可以设置多个参数。通常,您将使用以下三个:
-
retries 单个传入请求应尝试的重试次数。该属性的默认值为3
-
statuses–应该重试的HTTP状态代码列表,使用org.springframework.http.HttpStatus枚举名称表示。
-
backoff–用于在后续重试尝试之间计算Spring Cloud Gateway超时的策略。默认情况下,此属性是禁用的。
让我们从最简单的场景开始-使用参数的默认值。在这种情况下,我们只需要GatewayFilter为路线设置一个名称Retry的filter就可以。
@ClassRule
publicstaticMockServerContainermockServer=newMockServerContainer();
@BeforeClass
publicstaticvoidinit(){
//设置系统参数
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容器
MockServerClientclient=newMockServerClient(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
TestRestTemplatetemplate;
@Test
publicvoidtestAccountService(){
LOGGER.info(\"Sending/1...\");
ResponseEntityr=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]:StartedGatewayRetryTestin3.18seconds(JVMrunningfor10.757)
15:00:49.252---[main]:Sending/1...
15:00:49.382---[ctor-http-nio-2]:Enteringretry-filter
15:00:49.520---[ctor-http-nio-3]:settingnewiterationinattr0
15:00:49.522---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration0,configuredretries3
15:00:49.523---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:00:49.523---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
15:00:49.571---[ctor-http-nio-3]:settingnewiterationinattr1
15:00:49.571---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration1,configuredretries3
15:00:49.571---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:00:49.571---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
15:00:49.584---[ctor-http-nio-3]:settingnewiterationinattr2
15:00:49.584---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration2,configuredretries3
15:00:49.584---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:00:49.584---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
15:00:49.633---[ctor-http-nio-3]:settingnewiterationinattr3
15:00:49.634---[ctor-http-nio-3]:exceedsMaxIterationstrue,iteration3,configuredretries3
15:00:49.646---[main]:Received:status->404,payload->null
java.lang.AssertionError:
Expected:200
Actual:404
<Clicktoseedifference>
现在,让我们设置一些更高级的配置。我们可以更改重试次数,并设置要重试的确切HTTP状态代码,而不是一系列代码。在我们的例子中,重试的状态代码是HTTP 500,因为它是由我们的模拟端点返回的。
默认触发重试的计算公式为:
backoff.firstBackoff <= prevBackoff * factor < =backoff.maxBackoff
- backoff.firstBackoff 开始重试的时间
- backoff.maxBackoff 最大重试的时间
- backoff.factor 重试因子
- basedOnPreviousValue当设置为false时候 计算重试值变成: firstBackoff * (factor ^ n)
- 当设置为true时候 计算重试值变成: prevBackoff * factor
@ClassRule
publicstaticMockServerContainermockServer=newMockServerContainer();
@BeforeClass
publicstaticvoidinit(){
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\");
MockServerClientclient=newMockServerClient(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\"));
//OTHERRULES
}
如果使用新配置再运行一次相同的测试,则日志看起来会有些不同。我在下面的图片中强调了最重要的区别。如您所见,仅HTTP 500状态的当前重试次数为10。设置重试策略后,第一次重试尝试在50毫秒后执行,第二次重试在100毫秒后进行,第三次重试在200毫秒后进行,依此类推。(日志记录时间会有一点延迟,大家可以改变factor进行验证)
15:24:01.133---[main]:Sending/1...
15:24:01.288---[ctor-http-nio-2]:Enteringretry-filter
##第一次请求时间419
15:24:01.419---[ctor-http-nio-3]:settingnewiterationinattr0
15:24:01.421---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration0,configuredretries3
15:24:01.422---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:24:01.423---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
##第一次重试时间494重试时间大概在494-419=75
15:24:01.494---[ctor-http-nio-3]:settingnewiterationinattr1
15:24:01.494---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration1,configuredretries3
15:24:01.494---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:24:01.494---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
##第二次重试时间623重试时间大概在623-494=129
15:24:01.623---[ctor-http-nio-3]:settingnewiterationinattr2
15:24:01.623---[ctor-http-nio-3]:exceedsMaxIterationsfalse,iteration2,configuredretries3
15:24:01.623---[ctor-http-nio-3]:retryableStatusCode:true,statusCode500INTERNAL_SERVER_ERROR,configuredstatuses[500INTERNAL_SERVER_ERROR],configuredseries[SERVER_ERROR]
15:24:01.623---[ctor-http-nio-3]:retryableMethod:true,httpMethodGET,configuredmethods[GET]
##第二次重试时间623重试时间大概在852-623=230
15:24:01.852---[ctor-http-nio-3]:settingnewiterationinattr3
15:24:01.852---[ctor-http-nio-3]:exceedsMaxIterationstrue,iteration3,configuredretries3
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
publicvoidtestAccountServiceFail(){
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]:Enteringretry-filter
15:41:16.077---[parallel-1]:settingnewiterationinattr0
15:41:16.078---[parallel-1]:exceedsMaxIterationsfalse,iteration0,configuredretries3
15:41:16.079---[parallel-1]:exceptionoritscauseisretryableorg.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException},configuredexceptions[classjava.io.IOException,classorg.springframework.cloud.gateway.support.TimeoutException]
15:41:16.239---[parallel-3]:settingnewiterationinattr1
15:41:16.240---[parallel-3]:exceedsMaxIterationsfalse,iteration1,configuredretries3
15:41:16.240---[parallel-3]:exceptionoritscauseisretryableorg.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException},configuredexceptions[classjava.io.IOException,classorg.springframework.cloud.gateway.support.TimeoutException]
15:41:16.500---[parallel-5]:settingnewiterationinattr2
15:41:16.500---[parallel-5]:exceedsMaxIterationsfalse,iteration2,configuredretries3
15:41:16.500---[parallel-5]:exceptionoritscauseisretryableorg.springframework.web.server.ResponseStatusException{cause=org.springframework.cloud.gateway.support.TimeoutException},configuredexceptions[classjava.io.IOException,classorg.springframework.cloud.gateway.support.TimeoutException]
15:41:17.058---[parallel-7]:settingnewiterationinattr3
15:41:17.059---[parallel-7]:exceedsMaxIterationstrue,iteration3,configuredretries3
15:41:17.128---[main]:Received:status->504,payload->Account(id=null,number=null)
END
下篇给大家介绍Spring Cloud Gateway的限流
欢迎关注公众号!公众号回复:
入群
,扫码加入我们交流群!