Spring Cloud升级之路 - Hoxton - 5. 实现微服务调用重试

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: Spring Cloud升级之路 - Hoxton - 5. 实现微服务调用重试

feign 实现重试


我们继续使用resilience4j实现重试,根据上一篇Spring Cloud升级之路 - Hoxton - 4. 使用Resilience4j实现实例级别的隔离与熔断,我们已经加载了RetryReqistry这个核心配置Bean

Retry相关的配置:create-and-configure-retry


微信图片_20220624124514.jpg


我们这里的配置是:

resilience4j.retry:
  configs:
    default:
      maxRetryAttempts: 2
      waitDuration: 1
      retryExceptions:
        - java.lang.Exception
    service-provider2:
      maxRetryAttempts: 4


参考之前我们的LoadBalancerConfig

//对于非返回200的接口,抛出异常
if (execute.status() != HttpStatus.OK.value()) {
    throw new ResponseWrapperException(execute.toString(), execute);
}


所以,只要配置了所有异常都重试,对于非200返回也会重试。

实现重试,需要在负载均衡器作用之前,由于Spring-Cloud中可能会有很多的胶水代码,所以利用实现FeignBlockingLoadBalancerClient的方式可能扩展性不太好,这里使用切面的方式,实现重试。

CustomizedCircuitBreakerAspect

//配置哪些包下的FeignClient进行重试,必须含有@FeignClient注解
@Around("execution(* com.github.hashjang.hoxton..*(..)) && @within(org.springframework.cloud.openfeign.FeignClient)")
public Object feignClientWasCalled(final ProceedingJoinPoint pjp) throws Throwable {
    boolean isGet = false;
    MethodSignature signature = (MethodSignature) pjp.getSignature();
    FeignClient annotation = signature.getMethod().getDeclaringClass().getAnnotation(FeignClient.class);
    String serviceName = annotation.value();
    if (StringUtils.isBlank(serviceName)) {
        serviceName = annotation.name();
    }
    //查看是否是GET请求
    RequestMapping requestMapping = signature.getMethod().getAnnotation(RequestMapping.class);
    if (requestMapping != null &&
            (requestMapping.method().length == 0 ||
                    Arrays.asList(requestMapping.method()).contains(RequestMethod.GET))
    ) {
        isGet = true;
    }
    GetMapping getMapping = signature.getMethod().getAnnotation(GetMapping.class);
    if (getMapping != null) {
        isGet = true;
    }
    Retry retry;
    try {
        retry = retryRegistry.retry(serviceName, serviceName);
    } catch (ConfigurationNotFoundException e) {
        retry = retryRegistry.retry(serviceName);
    }
    if (!isGet) {
        //非GET请求,只有在断路器打开的情况下,才会重试
        retry = Retry.of(serviceName, RetryConfig.from(retry.getRetryConfig()).retryExceptions().retryOnException(throwable -> {
            Throwable cause = throwable.getCause();
            if (cause instanceof CallNotPermittedException) {
                //对于断路器,不区分方法,都重试,因为没有实际调用
                log.info("retry on circuit breaker is on: {}", cause.getMessage());
                return true;
            }
            return false;
        }).build());
    }
    //对于GET请求,启用重试机制
    Supplier<Object> objectSupplier = Retry.decorateSupplier(retry, () -> {
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            ReflectionUtils.rethrowRuntimeException(throwable);
            return null;
        }
    });
    return Try.ofSupplier(objectSupplier).get();
}


Spring Cloud Gateway 实现重试


Spring Cloud Gateway 默认有自己的重试,并且resilience4jRetry和 Spring Cloud Gateway 的 Reactor 机制是不兼容的,所以需要写一些额外的胶水代码,这里为了简便,就使用 Spring Cloud Gateway 默认有自己的重试。利用这个重试实现重试Filter插入到 Spring Cloud Gateway 中。


Spring Cloud Gateway 的重试Filter通过RetryGatewayFilterFactory实现,我们想对每个微服务调用生效,将他做成一个GlobalFilter.并且这个重试需要在负载均衡选择实例之前,所以,这个重试,必须要在RouteToRequestUrlFilter还有LoadBalancerClientFilter之前(这两个负责对于lb:路由查询负载均衡器获取实例重写 URL )。


我们想实现不同微服务不同配置,于是我们生成配置类ApiGatewayRetryConfig

@Data
@ConfigurationProperties(prefix = "spring.cloud.gateway")
public class ApiGatewayRetryConfig {
    private Map<String, RetryGatewayFilterFactory.RetryConfig> retry;
    public RetryGatewayFilterFactory.RetryConfig getDefault() {
        return retry.computeIfAbsent("default", key -> new RetryGatewayFilterFactory.RetryConfig());
    }
}


其中的RetryConfig包含如下我们使用到的属性:


配置项默认值说明retries3最大重试次数,不包括本身那次调用seriesSERVER_ERROR对于哪些响应码重试,默认是所有的5XX响应码statusesempty对于哪些状态码重试,这个是具体的 HttpStatus,和 series 之间只能指定一个methodsGET对于哪些 HttpMethod 重试。exceptionsList.of(IOException.class, TimeoutException.class)对于哪些异常重试,默认是IO异常和超时异常backoffbackOff配置,决定之后如何重试- firstBackoff5[ms]第一次重试间隔- maxBackoff最大重试间隔- factor2每次的重试间隔 = firstBackoff * (factor ^ (次数 - 1)),最大是maxBackoff- basedOnPreviousValuetrue是否基于上次请求的 backoff,如果是,则保留上次 backoff 时间,下次从这个 backoff 时间开始作为第一次重试间隔


我们的配置:

spring:
  cloud:
    gateway:
      # 这是我们自定义的重试配置:ApiGatewayRetryConfig,
      retry:
        default:
          retries: 1
          series: SERVER_ERROR,CLIENT_ERROR
          methods: GET
          # 对于断路器打开也会重试
          exceptions: io.github.resilience4j.circuitbreaker.CallNotPermittedException, org.springframework.cloud.gateway.support.TimeoutException, java.io.IOException
          backoff:
            basedOnPreviousValue: true
            factor: 2
            firstBackoff: 100ms
            maxBackoff: 500ms
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
16天前
|
Java 对象存储 开发者
解析Spring Cloud与Netflix OSS:微服务架构中的左右手如何协同作战
Spring Cloud与Netflix OSS不仅是现代微服务架构中不可或缺的一部分,它们还通过不断的技术创新和社区贡献推动了整个行业的发展。无论是对于初创企业还是大型组织来说,掌握并合理运用这两套工具,都能极大地提升软件系统的灵活性、可扩展性以及整体性能。随着云计算和容器化技术的进一步普及,Spring Cloud与Netflix OSS将继续引领微服务技术的发展潮流。
31 0
|
2天前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
11 1
|
2天前
|
安全 Java 对象存储
安全性考量:Spring Security与Netflix OSS在微服务安全中的作用
安全性考量:Spring Security与Netflix OSS在微服务安全中的作用
9 1
|
14天前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
27 5
|
13天前
|
前端开发 API 微服务
SpringCloud微服务之间使用Feign调用不通情况举例
SpringCloud微服务之间使用Feign调用不通情况举例
77 2
|
16天前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
34 1
|
27天前
|
监控 Java Nacos
SpringCloud基础5——微服务保护、Sentinel
sentinel、雪崩问题、流量控制、隔离和降级、授权规则、规则持久化
SpringCloud基础5——微服务保护、Sentinel
|
1月前
|
前端开发 Java UED
"揭秘!如何以戏剧性姿态,利用SpringCloud铸就无懈可击的异常处理铁壁,让你的微服务架构稳如泰山,震撼业界!"
【9月更文挑战第8天】随着微服务架构的普及,Spring Cloud作为一套完整的微服务解决方案被广泛应用。在微服务架构中,服务间调用频繁且复杂,异常处理成为保障系统稳定性和用户体验的关键。传统的异常处理方式导致代码冗余,降低系统可维护性和一致性。因此,基于Spring Cloud封装统一的异常处理机制至关重要。这样不仅可以减少代码冗余、提升一致性,还增强了系统的可维护性,并通过统一的错误响应格式优化了用户体验。具体实现包括定义全局异常处理器、自定义业务异常以及在服务中抛出这些异常。这种方式体现了微服务架构中的“服务治理”和“契约先行”原则,有助于构建健壮、可扩展的系统。
51 2
|
1月前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
59 2
|
1月前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
106 2