序
本文主要研究一下spring cloud gateway的RetryGatewayFilter
GatewayAutoConfiguration
spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
//......
@Bean
public RetryGatewayFilterFactory retryGatewayFilterFactory() {
return new RetryGatewayFilterFactory();
}
//......
}
默认启用了RetryGatewayFilterFactory
RetryGatewayFilterFactory
spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java
public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<RetryGatewayFilterFactory.RetryConfig> {
private static final Log log = LogFactory.getLog(RetryGatewayFilterFactory.class);
public RetryGatewayFilterFactory() {
super(RetryConfig.class);
}
@Override
public GatewayFilter apply(RetryConfig retryConfig) {
retryConfig.validate();
Predicate<? super RepeatContext<ServerWebExchange>> predicate = context -> {
ServerWebExchange exchange = context.applicationContext();
if (exceedsMaxIterations(exchange, retryConfig)) {
return false;
}
HttpStatus statusCode = exchange.getResponse().getStatusCode();
HttpMethod httpMethod = exchange.getRequest().getMethod();
boolean retryableStatusCode = retryConfig.getStatuses().contains(statusCode);
if (!retryableStatusCode && statusCode != null) { // null status code might mean a network exception?
// try the series
retryableStatusCode = retryConfig.getSeries().stream()
.anyMatch(series -> statusCode.series().equals(series));
}
boolean retryableMethod = retryConfig.getMethods().contains(httpMethod);
return retryableMethod && retryableStatusCode;
};
Repeat<ServerWebExchange> repeat = Repeat.onlyIf(predicate)
.doOnRepeat(context -> reset(context.applicationContext()));
//TODO: support timeout, backoff, jitter, etc... in Builder
Predicate<RetryContext<ServerWebExchange>> retryContextPredicate = context -> {
if (exceedsMaxIterations(context.applicationContext(), retryConfig)) {
return false;
}
for (Class<? extends Throwable> clazz : retryConfig.getExceptions()) {
if (clazz.isInstance(context.exception())) {
return true;
}
}
return false;
};
Retry<ServerWebExchange> reactorRetry = Retry.onlyIf(retryContextPredicate)
.doOnRetry(context -> reset(context.applicationContext()))
.retryMax(retryConfig.getRetries());
return apply(repeat, reactorRetry);
}
public boolean exceedsMaxIterations(ServerWebExchange exchange, RetryConfig retryConfig) {
Integer iteration = exchange.getAttribute("retry_iteration");
//TODO: deal with null iteration
return iteration != null && iteration >= retryConfig.getRetries();
}
public void reset(ServerWebExchange exchange) {
//TODO: what else to do to reset SWE?
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_ALREADY_ROUTED_ATTR);
}
@Deprecated
public GatewayFilter apply(Repeat<ServerWebExchange> repeat) {
return apply(repeat, Retry.onlyIf(ctxt -> false));
}
public GatewayFilter apply(Repeat<ServerWebExchange> repeat, Retry<ServerWebExchange> retry) {
return (exchange, chain) -> {
log.trace("Entering retry-filter");
int iteration = exchange.getAttributeOrDefault("retry_iteration", -1);
exchange.getAttributes().put("retry_iteration", iteration + 1);
return Mono.fromDirect(chain.filter(exchange)
.log("retry-filter", Level.INFO)
.retryWhen(retry.withApplicationContext(exchange))
.repeatWhen(repeat.withApplicationContext(exchange)));
};
}
//......
}
- 可以看到这个filter使用了reactor的Retry组件,同时往exchange的attribues添加retry_iteration,用来记录重试次数,该值默认从-1开始,第一次执行的时候,retry_iteration+1为0。之后每重试一次,就添加1。
- filter的apply接收两个参数,一个是Repeat<ServerWebExchange>,一个是Retry<ServerWebExchange>。
- repeat与retry的区别是repeat是在onCompleted的时候会重试,而retry是在onError的时候会重试。这里由于不一定是异常的时候才可能重试,所以加了repeat。
RetryConfig
spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/filter/factory/RetryGatewayFilterFactory.java
public static class RetryConfig {
private int retries = 3;
private List<Series> series = toList(Series.SERVER_ERROR);
private List<HttpStatus> statuses = new ArrayList<>();
private List<HttpMethod> methods = toList(HttpMethod.GET);
private List<Class<? extends Throwable>> exceptions = toList(IOException.class);
//......
public void validate() {
Assert.isTrue(this.retries > 0, "retries must be greater than 0");
Assert.isTrue(!this.series.isEmpty() || !this.statuses.isEmpty(),
"series and status may not both be empty");
Assert.notEmpty(this.methods, "methods may not be empty");
}
//......
}
可以看到配置文件有5个属性,如下:
- retries,默认为3,用来标识重试次数
- series,用来指定哪些段的状态码需要重试,默认SERVER_ERROR即5xx
- statuses,用于指定哪些状态需要重试,默认为空,它跟series至少得指定一个
- methods,用于指定那些方法的请求需要重试,默认为GET
- exceptions,用于指定哪些异常需要重试,默认为java.io.IOException
实例
spring:
cloud:
gateway:
routes:
- id: retry-demo
uri: http://localhost:9090
predicates:
- Path=/retry/**
filters:
- name: Retry
args:
retries: 15
series:
- SERVER_ERROR
- CLIENT_ERROR
methods:
- GET
- POST
exceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
这里指定了针对4xx及5xx的GET或POST方法或者IOException或TimeoutException的时候进行重试,重试次数为15次。
小结
RetryGatewayFilter借助了reactor-addons的retry组件进行了重试,主要使用了Mono的repeatWhen及retryWhen方法,前者在onCompleted的时候触发,后者在onError的时候触发。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。