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);
    };
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Cloud Gateway使用Token验证详解

    引入依赖 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <ty

  • 详解SpringCloud Finchley Gateway 统一异常处理

    SpringCloud Finchley Gateway 统一异常处理 全文搜索[@@]搜索重点内容标记 1 . 问题:使用SpringCloud Gateway时,会出现各种系统级异常,默认返回HTML. 2 . Finchley版本的Gateway,使用WebFlux形式作为底层框架,而不是Servlet容器,所以常规的异常处理无法使用 翻阅源码,默认是使用DefaultErrorWebExceptionHandler这个类实现结构如下: 可以实现参考DefaultErrorWebExcep

  • Spring Cloud Gateway全局异常处理的方法详解

    前言 Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式.Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等

  • 详解SpringCloud Gateway之过滤器GatewayFilter

    在Spring-Cloud-Gateway之请求处理流程文中我们了解最终网关是将请求交给过滤器链表进行处理,接下来我们阅读Spring-Cloud-Gateway的整个过滤器类结构以及主要功能 通过源码可以看到Spring-Cloud-Gateway的filter包中吉接口有如下三个,GatewayFilter,GlobalFilter,GatewayFilterChain,下来我依次阅读接口的主要实现功能. GatewayFilterChain 类图 代码 /** * 网关过滤链表接口 * 用

  • spring cloud gateway 全局过滤器的实现

    全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等. 接口定义类:org.springframework.cloud.gateway.filter.GlobalFilter public interface GlobalFilter { Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); } gateway自带的GlobalFilte

  • Spring Cloud Gateway入门解读

    Spring Cloud Gateway介绍 前段时间刚刚发布了Spring Boot 2正式版,Spring Cloud Gateway基于Spring Boot 2,是Spring Cloud的全新项目,该项目提供了一个构建在Spring 生态之上的API网关,包括:Spring 5,Spring Boot 2和Project Reactor. Spring Cloud Gateway旨在提供一种简单而有效的途径来发送API,并为他们提供横切关注点,例如:安全性,监控/指标和弹性.当前最新的

  • spring cloud gateway 限流的实现与原理

    在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击. 常见的限流方式,比如Hystrix适用线程池隔离,超过线程池的负载,走熔断的逻辑.在一般应用服务器中,比如tomcat容器也是通过限制它的线程数来控制并发的:也有通过时间窗口的平均速度来控制流量.常见的限流纬度有比如通过Ip来限流.通过uri来限流.通过用户访问频次来限流. 一般限流都是在网关这一层做,比如Nginx.Openresty.kong.zuul.Spring

  • Spring Cloud Gateway重试机制的实现

    前言 重试,我相信大家并不陌生.在我们调用Http接口的时候,总会因为某种原因调用失败,这个时候我们可以通过重试的方式,来重新请求接口. 生活中这样的事例很多,比如打电话,对方正在通话中啊,信号不好啊等等原因,你总会打不通,当你第一次没打通之后,你会打第二次,第三次...第四次就通了. 重试也要注意应用场景,读数据的接口比较适合重试的场景,写数据的接口就需要注意接口的幂等性了.还有就是重试次数如果太多的话会导致请求量加倍,给后端造成更大的压力,设置合理的重试机制才是最关键的. 今天我们来简单的了

  • Spring Cloud Gateway重试机制原理解析

    重试,我相信大家并不陌生.在我们调用Http接口的时候,总会因为某种原因调用失败,这个时候我们可以通过重试的方式,来重新请求接口. 生活中这样的事例很多,比如打电话,对方正在通话中啊,信号不好啊等等原因,你总会打不通,当你第一次没打通之后,你会打第二次,第三次-第四次就通了. 重试也要注意应用场景,读数据的接口比较适合重试的场景,写数据的接口就需要注意接口的幂等性了.还有就是重试次数如果太多的话会导致请求量加倍,给后端造成更大的压力,设置合理的重试机制才是最关键的. 今天我们来简单的了解下Spr

  • 详解Spring Cloud Zuul重试机制探秘

    简介 本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下: 开启Zuul功能 通过源码了解Zuul的一次转发 怎么开启zuul的重试机制 Edgware.RC1版本的优化 开启Zuul的功能 首先如何使用spring cloud zuul完成路由转发的功能,这个问题很简单,只需要进行如下准备工作即可: 注册中心(Eureka Server) zuul(同时也是Eureka Client) 应用服务(同时也是Eureka Client) 我们希望zuul和后端的应用

  • Spring Cloud 请求重试机制核心代码分析

    场景 发布微服务的操作一般都是打完新代码的包,kill掉在跑的应用,替换新的包,启动. spring cloud 中使用eureka为注册中心,它是允许服务列表数据的延迟性的,就是说即使应用已经不在服务列表了,客户端在一段时间内依然会请求这个地址.那么就会出现请求正在发布的地址,而导致失败. 我们会优化服务列表的刷新时间,以提高服务列表信息的时效性.但是无论怎样,都无法避免有那么一段时间是数据不一致的. 所以我们想到一个办法就是重试机制,当a机子在重启时,同个集群的b是可以正常提供服务的,如果有

  • spring cloud gateway集成hystrix全局断路器操作

    gateway集成hystrix全局断路器 pom.xml添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> 在配置文件中,增加spring.cloud.gateway.default-filters: defa

  • spring cloud gateway 如何修改请求路径Path

    一.背景 项目升级改造,老项目使用请求url中特定参数进行服务路由,现使用gateway网关进行路由服务信息 二.根据参数信息修改请求路径Path @Component public class RequestFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpR

  • Nacos+Spring Cloud Gateway动态路由配置实现步骤

    目录 前言 一.Nacos环境准备 1.启动Nacos配置中心并创建路由配置 2.连接Nacos配置中心 二.项目构建 1.项目结构 2.编写测试代码 三.测试动态网关配置 1.启动服务,观察注册中心 2.访问网关,观察服务日志 四.总结 前言 Nacos最近项目一直在使用,其简单灵活,支持更细粒度的命令空间,分组等为麻烦复杂的环境切换提供了方便:同时也很好支持动态路由的配置,只需要简单的几步即可.在国产的注册中心.配置中心中比较突出,容易上手,本文通过gateway.nacos-consume

  • Spring Cloud Gateway整合sentinel 实现流控熔断的问题

    目录 一.什么是网关限流: 二.gateway 整合 sentinel 实现网关限流: 三.sentinel 网关流控规则的介绍: 3.1.网关流控规则: 3.2.API 分组管理: 四.sentinel 网关流控实现的原理: 五.网关限流了,服务就安全了吗? 六.自定义流控异常消息: 一.什么是网关限流: 在微服务架构中,网关层可以屏蔽外部服务直接对内部服务进行调用,对内部服务起到隔离保护的作用,网关限流,顾名思义,就是通过网关层对服务进行限流,从而达到保护后端服务的作用. Sentinel

  • Spring Cloud Gateway动态路由Apollo实现详解

    目录 背景 路由的加载 实现动态路由 背景 在之前我们了解的Spring Cloud Gateway配置路由方式有两种方式 通过配置文件 spring: cloud: gateway: routes: - id: test predicates: - Path=/ms/test/* filters: - StripPrefix=2 uri: http://localhost:9000 通过JavaBean @Bean public RouteLocator routeLocator(RouteL

随机推荐