功能区重试配置不起作用

Ribbon Retry Config not working

我有Spring云打包的ZuulAPI网关运行下面的配置-

# Eureka Client Config to register ZUUL with Eureka
eureka:
client:
    healthcheck:
      enabled: true
    lease:
      duration: 5
    service-url:
      defaultZone: http://localhost:8761/eureka/

# Ribbon Global Config
ribbon:
  OkToRetryOnAllOperations: false
  ReadTimeout: 30000
  ConnectTimeout: 1000
  MaxTotalHttpConnections: 1600
  MaxConnectionsPerHost: 800
  MaxAutoRetries: 11
  MaxAutoRetriesNextServer: 111

# Ribbon Named Client Config for Ingest API
ingestService:
  ribbon:
    eureka:
      enabled: false
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: http://test-nlb-zuul-us-west-2c-6af11a3ede8a872a.elb.us-west-2.amazonaws.com
    OkToRetryOnAllOperations: true
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 1
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 200
    retryableStatusCodes: 500, 501, 502, 503
    ReadTimeout: 10000
    ConnectTimeout: 1000

# Zuul Routes
zuul:
  debug:
    request: true
    parameter: true
  host: # timeout config for direct URL based requests from Zuul to external URLs
    connect-timeout-millis: 10000
    socket-timeout-millis: 20000
  ignored-services: '*'
  routes:
    ingest:
      path: /ingest/**
      retryable: true
      stripPrefix: false
      serviceId: ingestService

management.security.enabled : false

spring:
  application:
    name: zuul-gateway
  cloud:
    loadbalancer:
      retry:
        enabled: true

logging:
  level:
    org:
      springframework:
        retry: DEBUG
      apache:
        http: DEBUG
    com:
      netflix:
        ribbon: DEBUG
        eureka: DEBUG
        discovery: DEBUG

hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: THREAD
          thread:
            timeoutInMilliseconds: 30000

当我点击 zuul /ingest 端点时,请求被重定向到 ingestService 的功能区 serviceId 配置下列出的服务器。但是,我看到功能区重试配置被完全忽略了。

当服务器暂时出现 HTTP 500 错误时,全局功能区配置 - ribbon.MaxAutoRetries=11 和命名的客户端配置 - ingestService.ribbon.MaxAutoRetries=1 都将被忽略。我看到重试发生 恰好 10 次 我不知道重试配置来自哪里,这似乎完全违背了可用的文档。我不确定从哪里开始调试,因为我是整个 netflix 工具生态系统的新手。想检查我是否犯了一些配置错误。请指教

这是我的 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mycomp</groupId>
    <artifactId>zuul-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>zuul-gateway</name>
    <description>Spring Boot Zuul</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

和spring应用程序启动

package com.mycomp.zuulgateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ZuulGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApplication.class, args);
    }
}

当我开始这个项目时,我从

开始
  • spring-cloud-dependencies 版本 Edgware.SR1

因此,根据 pom/bom 声明 - 我的功能区应用程序客户端似乎正在使用 spring-cloud-netflix-core-1.4.2.RELEASE.jar

spring-cloud-netflix-core-1.4.2.RELEASE.jar 中,class org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient 有一个 bug/defect -

来自方法 -

public RibbonApacheHttpResponse execute(final RibbonApacheHttpRequest request, final IClientConfig configOverride) throws Exception {
    Builder builder = RequestConfig.custom();
    IClientConfig config = configOverride != null ? configOverride : this.config;
    builder.setConnectTimeout((Integer)config.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout));
    builder.setSocketTimeout((Integer)config.get(CommonClientConfigKey.ReadTimeout, this.readTimeout));
    builder.setRedirectsEnabled((Boolean)config.get(CommonClientConfigKey.FollowRedirects, this.followRedirects));
    final RequestConfig requestConfig = builder.build();
    final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
    RetryCallback retryCallback = new RetryCallback() {
        public RibbonApacheHttpResponse doWithRetry(RetryContext context) throws Exception {
            RibbonApacheHttpRequest newRequest = request;
            if (context instanceof LoadBalancedRetryContext) {
                ServiceInstance service = ((LoadBalancedRetryContext)context).getServiceInstance();
                if (service != null) {
                    newRequest = newRequest.withNewUri(new URI(service.getUri().getScheme(), newRequest.getURI().getUserInfo(), service.getHost(), service.getPort(), newRequest.getURI().getPath(), newRequest.getURI().getQuery(), newRequest.getURI().getFragment()));
                }
            }

            // ***** after getting a newRequest in the if block above, the newRequest is not passed to the getSecureRequest() below ***** 
            newRequest = RetryableRibbonLoadBalancingHttpClient.this.getSecureRequest(request, configOverride);
            // the above should have been -
            // newRequest = RetryableRibbonLoadBalancingHttpClient.this.getSecureRequest(newRequest, configOverride);

            HttpUriRequest httpUriRequest = newRequest.toRequest(requestConfig);
            HttpResponse httpResponse = ((CloseableHttpClient)RetryableRibbonLoadBalancingHttpClient.this.delegate).execute(httpUriRequest);
            if (retryPolicy.retryableStatusCode(httpResponse.getStatusLine().getStatusCode())) {
                if (CloseableHttpResponse.class.isInstance(httpResponse)) {
                    ((CloseableHttpResponse)httpResponse).close();
                }

                throw new RetryableStatusCodeException(RetryableRibbonLoadBalancingHttpClient.this.clientName, httpResponse.getStatusLine().getStatusCode());
            } else {
                return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
            }
        }
    };
    return this.executeWithRetry(request, retryPolicy, retryCallback);
}

由于上面代码中的错误,尽管功能区配置(对于 maxAutoRetries 和 maxAutoRetriesNextServer)是基于 yaml 文件设置的,但是对具有下一个服务器 uri 的请求对象的更新被忽略并且总是违背同一台服务器并导致副作用。

这似乎已在 spring-cloud-netflix-core-1.4.3.RELEASE.jar 中修复。

因此,更新 pom/bom

  • spring-cloud-dependencies 版本 Edgware.SR2

将功能区客户端依赖项更新为 spring-cloud-netflix-core-1.4.3.RELEASE.jar,此问题现已解决。