功能区重试配置不起作用
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
,此问题现已解决。
我有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
,此问题现已解决。