SpringGateway使用loadbalance支持重试配置

使用webClient进行接口调用,在初始话webClient时添加 @Loadbalance注解

spring 自动装配置就会自动注入默认的filter

类:ReactorLoadBalancerClientAutoConfiguration

会自动注入 webClient 【RetryableLoadBalancerExchangeFilterFunction】 过滤器和重试策略【 RetryableExchangeFilterFunctionLoadBalancerRetryPolicy 重试策略】

    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true")
    @Bean
    public RetryableLoadBalancerExchangeFilterFunction retryableLoadBalancerExchangeFilterFunction(
            ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory,
            LoadBalancerRetryPolicy.Factory retryPolicyFactory,
            ObjectProvider<List<LoadBalancerClientRequestTransformer>> transformers) {
        return new RetryableLoadBalancerExchangeFilterFunction(retryPolicyFactory, loadBalancerFactory,
                transformers.getIfAvailable(Collections::emptyList));
    }

    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true")
    @Bean
    public LoadBalancerRetryPolicy.Factory loadBalancerRetryPolicy(
            ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
        return new RetryableExchangeFilterFunctionLoadBalancerRetryPolicy.Factory(loadBalancerFactory);
    }

当发起http 请求时就会执行org.springframework.cloud.client.loadbalancer.reactive.RetryableLoadBalancerExchangeFilterFunction#filter 方法中

@Override
    public Mono<ClientResponse> filter(ClientRequest clientRequest, ExchangeFunction next) {
        URI originalUrl = clientRequest.url();
        String serviceId = originalUrl.getHost();
        if (serviceId == null) {
            String message = String.format("Request URI does not contain a valid hostname: %s", originalUrl.toString());
            if (LOG.isWarnEnabled()) {
                LOG.warn(message);
            }
            return Mono.just(ClientResponse.create(HttpStatus.BAD_REQUEST).body(message).build());
        }
        LoadBalancerRetryContext loadBalancerRetryContext = new LoadBalancerRetryContext(clientRequest);
        LoadBalancerProperties properties = loadBalancerFactory.getProperties(serviceId);

        Retry exchangeRetry = buildRetrySpec(properties.getRetry().getMaxRetriesOnSameServiceInstance(), true,
                properties.getRetry());
        Retry filterRetry = buildRetrySpec(properties.getRetry().getMaxRetriesOnNextServiceInstance(), false,
                properties.getRetry());
        LoadBalancerRetryPolicy retryPolicy = retryPolicyFactory.apply(serviceId);

        Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
                .getSupportedLifecycleProcessors(
                        loadBalancerFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
                        RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
        String hint = getHint(serviceId, properties.getHint());
        RequestData requestData = new RequestData(clientRequest);
        DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
                new RetryableRequestContext(null, requestData, hint));
        supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));

        //choose 默认使用轮询算法,选取一个节点
        return Mono.defer(() -> choose(serviceId, lbRequest).flatMap(lbResponse -> {
           //实例信息
            ServiceInstance instance = lbResponse.getServer();
            lbRequest.setContext(new RetryableRequestContext(instance, requestData, hint));
            if (instance == null) {
                String message = serviceInstanceUnavailableMessage(serviceId);
                if (LOG.isWarnEnabled()) {
                    LOG.warn(message);
                }
                supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
                        .onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
                                CompletionContext.Status.DISCARD, lbRequest, lbResponse)));
                return Mono.just(ClientResponse.create(HttpStatus.SERVICE_UNAVAILABLE)
                        .body(serviceInstanceUnavailableMessage(serviceId)).build());
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("LoadBalancer has retrieved the instance for service %s: %s", serviceId,
                        instance.getUri()));
            }
            LoadBalancerProperties.StickySession stickySessionProperties = properties.getStickySession();
            ClientRequest newRequest = buildClientRequest(clientRequest, instance,
                    stickySessionProperties.getInstanceIdCookieName(),
                    stickySessionProperties.isAddServiceInstanceCookie(), transformers);
            supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse));
            return next.exchange(newRequest)
                    //请求执行失败
                    .doOnError(throwable -> supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
                            .onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
                                    CompletionContext.Status.FAILED, throwable, lbRequest, lbResponse))))          //请求执行成功         
                     .doOnSuccess(clientResponse -> supportedLifecycleProcessors.forEach(
                            lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
                                    lbRequest, lbResponse, new ResponseData(clientResponse, requestData)))))
                    .map(clientResponse -> {
                        loadBalancerRetryContext.setClientResponse(clientResponse);
                        //相同节点重试     
                        if (shouldRetrySameServiceInstance(retryPolicy, loadBalancerRetryContext)) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug(String.format("Retrying on status code: %d",
                                        clientResponse.statusCode().value()));
                            }
                            throw new RetryableStatusCodeException();
                        }
                        return clientResponse;

                    });
        }).map(clientResponse -> {
            loadBalancerRetryContext.setClientResponse(clientResponse);
             选取一个新的节点
            if (shouldRetryNextServiceInstance(retryPolicy, loadBalancerRetryContext)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("Retrying on status code: %d", clientResponse.statusCode().value()));
                }
                throw new RetryableStatusCodeException();
            }
            return clientResponse;

        }).retryWhen(exchangeRetry)).retryWhen(filterRetry);
    }

参数详细解:

retryableStatusCodes

默认情况下,Ribbon不会针对HTTP非正常响应状态值(如404、502等)进行重试。如果您需要对特殊的Http状态进行重试,需要配置该参数。

OkToRetryOnAllOperations

这个参数指的是是否允许所有的HTTP请求(GET,POST,PUT等)重试。默认值是false,只允许GET请求重试。对于POST等请求,请慎重使用。

MaxAutoRetries

这个参数用于配置当前实例最大重试次数,默认值为0。重试次数不包括第一次请求。

MaxAutoRetriesNextServer

这个参数指的是切换实例最大重试次数,默认值1。

如果访问当前实例异常,会再次尝试访问当前实例(次数由MaxAutoRetries决定);如果还不行,就会访问下一个实例;如果仍然不行,会把下一个实例作为当前实例并重试(次数由MaxAutoRetries决定)...依此类推,直到切换实例次数达到上限(由MaxAutoRetriesNextServer决定)。总共的重试次数计算公式:

- spring.cloud.loadbalancer.retry.enabled:启用或禁用重试。默认为 false。

- spring.cloud.loadbalancer.retry.repeat-services:是否在同一服务实例上重试。默认为 false。

- spring.cloud.loadbalancer.retry.retryable-status-codes:可重试的HTTP状态码。默认为 500,502,503。

- spring.cloud.loadbalancer.retry.backoff.first-backoff:第一次重试尝试之前等待的时间。默认为 1000ms。

- spring.cloud.loadbalancer.retry.backoff.max-backoff:重试之间等待的最长时间。默认为 2000ms。

- spring.cloud.loadbalancer.retry.backoff.multiplier:退避乘数。默认为 1.1。

重试方案2

添加依赖

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.1</version>
</dependency>

添加配置

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://example.com
          predicates:
            - Path=/my/path/**
          filters:
            - RewritePath=/my/path/(?<remaining>.*), /$\{remaining}
            - name: Retry
              args:
                retries: 3
                statuses: BAD_GATEWAY
                backoff:
                  firstBackoff: 1000ms
                  maxBackoff: 10000ms
                  factor: 2.0
          metadata:
            connectTimeout: 5000
            readTimeout: 10000

为my_route路由配置重试,最多重试3次,仅在收到BAD_GATEWAY响应时重试。配置了指数退避策略,第一次重试之前等待1秒,最多等待10秒,并且每次重试的等待时间是前一次的2倍。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值