Spring Cloud Gateway重试机制
前言
重试,我相信大家并不陌生。在我们调用Http接口的时候,总会因为某种原因调用失败,这个时候我们可以通过重试的方式,来重新请求接口。
生活中这样的事例很多,比如打电话,对方正在通话中啊,信号不好啊等等原因,你总会打不通,当你第一次没打通之后,你会打第二次,第三次...第四次就通了。
重试也要注意应用场景,读数据的接口比较适合重试的场景,写数据的接口就需要注意接口的幂等性了。还有就是重试次数如果太多的话会导致请求量加倍,给后端造成更大的压力,设置合理的重试机制才是最关键的。
今天我们来简单的了解下Spring Cloud Gateway中的重试机制和使用。
使用讲解
RetryGatewayFilter是Spring Cloud Gateway对请求重试提供的一个GatewayFilter Factory。
配置方式:
spring:
cloud:
gateway:
routes:
- id: fsh-house
uri: lb://fsh-house
predicates:
- Path=/house/**
filters:
- name: Retry
args:
retries: 3
series:
- SERVER_ERROR
statuses:
- OK
methods:
- GET
- POST
exceptions:
- java.io.IOException
配置讲解
配置类源码org.springframework.cloud.gateway.filter.factory.RetryGatewayFilterFactory.RetryConfig:
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);
// .....
}
- retries:重试次数,默认值是3次
- series:状态码配置(分段),符合的某段状态码才会进行重试逻辑,默认值是SERVER_ERROR,值是5,也就是5XX(5开头的状态码),共有5个值:
public enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
REDIRECTION(3),
CLIENT_ERROR(4),
SERVER_ERROR(5);
}
- statuses:状态码配置,和series不同的是这边是具体状态码的配置,取值请参考:org.springframework.http.HttpStatus
- methods:指定哪些方法的请求需要进行重试逻辑,默认值是GET方法,取值如下:
public enum HttpMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
}
- exceptions:指定哪些异常需要进行重试逻辑,默认值是java.io.IOException
代码测试
就写个接口,在接口中记录请求次数,然后抛出一个异常模拟500,通过网关访问这个接口,如果你配置了重试次数是3,那么接口中会输出4次结果才是对的,证明重试生效了。
AtomicInteger ac = new AtomicInteger();
@GetMapping("/data")
public HouseInfo getData(@RequestParam("name") String name) {
if (StringUtils.isBlank(name)) {
throw new RuntimeException("error");
}
System.err.println(ac.addAndGet(1));
return new HouseInfo(1L, "上海", "虹口", "XX小区");
}
更多Spring Cloud代码尽在:https://github.com/yinjihuan/spring-cloud
源码欣赏
@Override
public GatewayFilter apply(RetryConfig retryConfig) {
// 验证重试配置格式是否正确
retryConfig.validate();
Repeat<ServerWebExchange> statusCodeRepeat = null;
if (!retryConfig.getStatuses().isEmpty() || !retryConfig.getSeries().isEmpty()) {
Predicate<RepeatContext<ServerWebExchange>> repeatPredicate = 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;
};
statusCodeRepeat = Repeat.onlyIf(repeatPredicate)
.doOnRepeat(context -> reset(context.applicationContext()));
}
//TODO: support timeout, backoff, jitter, etc... in Builder
Retry<ServerWebExchange> exceptionRetry = null;
if (!retryConfig.getExceptions().isEmpty()) {
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;
};
// 使用reactor extra的retry组件
exceptionRetry = Retry.onlyIf(retryContextPredicate)
.doOnRetry(context -> reset(context.applicationContext()))
.retryMax(retryConfig.getRetries());
}
return apply(statusCodeRepeat, exceptionRetry);
}
public boolean exceedsMaxIterations(ServerWebExchange exchange, RetryConfig retryConfig) {
Integer iteration = exchange.getAttribute(RETRY_ITERATION_KEY);
//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);
}
public GatewayFilter apply(Repeat<ServerWebExchange> repeat, Retry<ServerWebExchange> retry) {
return (exchange, chain) -> {
if (log.isTraceEnabled()) {
log.trace("Entering retry-filter");
}
// chain.filter returns a Mono<Void>
Publisher<Void> publisher = chain.filter(exchange)
//.log("retry-filter", Level.INFO)
.doOnSuccessOrError((aVoid, throwable) -> {
// 获取已经重试的次数,默认值为-1
int iteration = exchange.getAttributeOrDefault(RETRY_ITERATION_KEY, -1);
// 增加重试次数
exchange.getAttributes().put(RETRY_ITERATION_KEY, iteration + 1);
});
if (retry != null) {
// retryWhen returns a Mono<Void>
// retry needs to go before repeat
publisher = ((Mono<Void>)publisher).retryWhen(retry.withApplicationContext(exchange));
}
if (repeat != null) {
// repeatWhen returns a Flux<Void>
// so this needs to be last and the variable a Publisher<Void>
publisher = ((Mono<Void>)publisher).repeatWhen(repeat.withApplicationContext(exchange));
}
return Mono.fromDirect(publisher);
};
}
欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course)
Spring Cloud Gateway重试机制的更多相关文章
- Spring Cloud 请求重试机制核心代码分析
场景 发布微服务的操作一般都是打完新代码的包,kill掉在跑的应用,替换新的包,启动. spring cloud 中使用eureka为注册中心,它是允许服务列表数据的延迟性的,就是说即使应用已经不在服 ...
- Spring Cloud学习 之 Spring Cloud Ribbon 重试机制及超时设置不生效
今天测了一下Ribbon的重试跟超时机制,发现进行的全局超时配置一直不生效,配置如下: ribbon: #单位ms,请求连接的超时时间,默认1000 ConnectTimeout: 500 #单位ms ...
- 一文详解Spring Cloud Feign重试机制
前言 Feign组件默认使用Ribbon的重试机制并增加了根据状态码判断重试机制,默认情况下是不启用的.Feign使用的是Spring Retry组件,需要引入依赖才能启用. 一.POM引入Sprin ...
- springcloud(十七):服务网关 Spring Cloud GateWay 熔断、限流、重试
上篇文章介绍了 Gataway 和注册中心的使用,以及 Gataway 中 Filter 的基本使用,这篇文章我们将继续介绍 Filter 的一些常用功能. 修改请求路径的过滤器 StripPrefi ...
- 网关服务Spring Cloud Gateway(三)
上篇文章介绍了 Gataway 和注册中心的使用,以及 Gataway 中 Filter 的基本使用,这篇文章我们将继续介绍 Filter 的一些常用功能. 修改请求路径的过滤器 StripPrefi ...
- 微服务网关实战——Spring Cloud Gateway
导读 作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用.本文对Spring Clou ...
- 微服务网关 Spring Cloud Gateway
1. 为什么是Spring Cloud Gateway 一句话,Spring Cloud已经放弃Netflix Zuul了.现在Spring Cloud中引用的还是Zuul 1.x版本,而这个版本是 ...
- 跟我学SpringCloud | 第十四篇:Spring Cloud Gateway高级应用
SpringCloud系列教程 | 第十四篇:Spring Cloud Gateway高级应用 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 ...
- Spring Cloud gateway 网关服务二 断言、过滤器
微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...
随机推荐
- 【VSFTP服务】vsftpd文件传输协议
vsftpd文件传输协议 系统环境:CentOS Linux release 7.6.1810 (Core) 一.简介 FTP(文件传输协议)全称是:Very Secure FTP Server. ...
- SATA接口、PCI/PCIe、NVMe的介绍
SATA接口.PCI/PCIe.NVMe的介绍 SATA接口 SATA是Serial ATA的缩写,即串行ATA. SATA已经完全取代旧式PATA(Parallel ATA或旧称IDE)接口的旧式硬 ...
- Solr的知识点学习
Solr单机版的安装与使用 1.Solr单机版的安装与使用,简单写了如何进行Solr的安装与使用.那么很多细节性问题,这里进行简单的介绍.我使用的是Solr与Tomcat整合配置. 2.什么是Solr ...
- C#使用FileSystemWatcher来监控指定文件夹,并使用TCP/IP协议通过Socket发送到另外指定文件夹
项目需求: 局域网内有两台电脑,电脑A(Windows系统)主要是负责接收一些文件(远程桌面粘贴.FTP上传.文件夹共享等方式),希望能在A接收文件后自动传输到电脑B(Windows系统)来做一个备份 ...
- winfrom 获取焦点控件
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Win ...
- 如何使用Postman发送get请求?
一.接口测试介绍 接口测试:就是针对软件对外提供服务的接口输入输出进行测试,以及接口间相互逻辑的测试,验证接口功能和接口描述文档的一致性. 接口测试好处:接口测试通常能对系统测试的更为彻底,更高的保障 ...
- Jmeter进阶技能-数据库信息,传递参数
因为项目的原因,假设我们要实现如下要求:从数据库的用户表里获取用户信息,并作为参数全部传递给登录请求,分别完成登录操作. 01Jmeter连接数据库 1.添加JDBC Connection Confi ...
- weblogic删除域
彻底删除weblogic域的方法: 例如:删除域名为:fm_ump的域 第一步,删除域注册记录: [bofm@UAT02-BIZ-ZJCG-AP-008 Middleware]$ cd /home/s ...
- 错误记录PHP preg_match(): Compilation failed: range out of order in character class at offset 7 01_login.php</b> on line <b>13</b><br />
<?php //响应头格式为json,编码为uft-8,注意有- header("Content-Type:application/json;charset=utf-8"); ...
- Git的认识与使用
Git教程 https://www.liaoxuefeng.com/wiki/896043488029600/897271968352576 Git与SVN区别 Git 不仅仅是个版本控制系统,它也是 ...