返回

Spring Cloud升级之路 - Hoxton - 9. 对网关非 Get 请求的重试与优化

见解分享

前言

在之前的系列《Spring Cloud升级之路 - Hoxton - 5. 实现微服务调用重试》中,我们针对 OpenFeign 和 Spring Cloud Gateway 都设置了重试。

  • Get请求:任何非200 响应码,任何异常,都会重试。
  • 非 Get 请求:任何IOException,都会重试。

这里存在一些问题,例如,Get 请求出现IOException,也不会重试。本文详细介绍了如何对 Spring Cloud Gateway 进行优化和针对非Get 请求进行重试。

重试优化

  1. 去除日志干扰
    日志对于我们开发及排查问题至关重要,特别对于网关来说,更是如此。但有些日志的干扰对我们来说并无多大意义,例如:

    2020-12-08 16:44:08.584  INFO 32338 --- [       Thread-7] c.netflix.zuul.web.ZuulController        : Cannot find connection to API-A: java.io.IOException: Connection reset by peer
    

    可以发现,这是因为 Zuul 未能从目标微服务获取响应,于是发起了重试,导致了大量的 Connection reset by peer 类型的日志。对于这些干扰性日志,我们可以通过以下方式去除:

    logging:
      level:
        com.netflix.zuul: ERROR
    
  2. 设置最大重试次数
    我们可以通过设置 ribbon.maxAutoRetriesribbon.maxAutoRetriesNextServer 来限制最大重试次数,还可以针对 Get 请求和非 Get 请求,分别限制最大重试次数。如下所示:

    ribbon:
      maxAutoRetries: 1  # Get 请求最大重试次数
      maxAutoRetriesNextServer: 2  # 非 Get 请求最大重试次数
    
  3. 设置重试间隔
    我们可以通过设置 ribbon.retryableStatusCodes 来设置重试间隔,例如,当返回状态码为 500 时,重试间隔为 1 秒,当返回状态码为 503 时,重试间隔为 2 秒,如下所示:

    ribbon:
      retryableStatusCodes:
        - 500:1000  # 当返回状态码为 500 时,重试间隔为 1 秒
        - 503:2000  # 当返回状态码为 503 时,重试间隔为 2 秒
    

针对非 Get 请求的重试

在之前的版本中,针对非 Get 请求的重试只针对 IOException 进行了设置,这显然是不够的,例如,当目标微服务宕机时,请求也会失败,这时就需要进行重试。

我们可以通过以下步骤来实现针对非 Get 请求的重试:

  1. 实现 GatewayFilterFactory 接口
  2. 重写 apply 方法
  3. 使用 CircuitBreaker 来实现重试
@Component
public class RetryFilter implements GatewayFilterFactory {

    private static final Logger log = LoggerFactory.getLogger(RetryFilter.class);

    private Resilience4JCircuitBreakerFactory circuitBreakerFactory;

    public RetryFilter(Resilience4JCircuitBreakerFactory circuitBreakerFactory) {
        this.circuitBreakerFactory = circuitBreakerFactory;
    }

    @Override
    public GatewayFilter apply(Config config) {
        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("retry");
        return (exchange, chain) -> circuitBreaker.run(() -> {
            try {
                return chain.filter(exchange);
            } catch (Exception e) {
                log.error("请求失败,触发重试", e);
                throw e;
            }
        });
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.emptyList();
    }

    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }

    public static class Config {

    }
}

apply 方法中,我们使用了 CircuitBreaker 来实现重试,当请求失败时,CircuitBreaker 会触发重试。

总结

本文详细介绍了如何对 Spring Cloud Gateway 进行优化和针对非Get 请求进行重试,希望能对大家有所帮助。