假设我有一个 Eureka Server ,一个 Spring Cloud Gateway ,一个 ServiceA 运行两个实例。
当我部署服务的更新版本时,eureka会在短时间内为 ServiceA 保留四个实例,不再存在的实例和刚刚启动的实例。< / p>
在Eureka驱逐网关上不再有效的两个实例之前,功能区仍将在不再存在的实例之间进行平衡,从而生成ConnectTimeoutException
,从而生成504 (Gateway Time-out)
。我已使用以下重试配置在网关中配置了这些路由。
val retry = RetryGatewayFilterFactory.RetryConfig()
.setExceptions(ConnectException::class.java)
这允许功能区在异常为ConnectException: Connection refused
时立即重试,但在ConnectTimeoutException
时不会重试。
我可以调整功能区和尤里卡客户端的刷新间隔,但我宁愿不接触它们。
所以,我有两个问题。
由于
答案 0 :(得分:1)
尝试ribbon.retryableStatusCodes=404,502,504
更新:
我首先想到ConnectException
应该与代码502
关联,并且504
是SocketTimeoutException
。如果有错,请纠正我。
对不起,没有更多地关注云网关,但是它的LB可以选择使用Ribbon。
假设您正在通过OKHttp
使用Ribbon和ribbon.OkHttp.enabled
。
OkHttpRibbonConfiguration
将初始化一个OkHttpLoadBalancingClient
bean,该bean执行请求。在其execute()
中,它在每个请求执行期间首先创建一个RetryCallback
匿名实现对象。 RetryCallback
负责执行请求并重试执行。
让我们看一下RetryCallback
的逻辑。收到响应后,它将检查响应状态代码是否可重试。如果类加载器中存在RetryTemplate
类,则loadBalancedRetryPolicyFactory
的实现为RibbonLoadBalancedRetryPolicyFactory
。用来创建RibbonLoadBalancedRetryPolicy
对象(下面的retryPlicy
)。
executeWithRetry()
用RetryTemplate
创建一个RetryPolicy
。 (前提条件是,通过将请求设置为可重试,在Spring Cloud Gateway中启用了重试)。
public OkHttpRibbonResponse execute(...) throws Exception {
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryPolicyFactory.create(this.getClientName(), this);
...
final Request request = newRequest.toRequest();
Response response = httpClient.newCall(request).execute();
if(retryPolicy.retryableStatusCode(response.code())) {
ResponseBody responseBody = response.peekBody(Integer.MAX_VALUE);
response.close();
throw new OkHttpStatusCodeException(RetryableOkHttpLoadBalancingClient.this.clientName,
response, responseBody, newRequest.getURI());
}
return new OkHttpRibbonResponse(response, newRequest.getUri());
}
private OkHttpRibbonResponse executeWithRetry(...) throws Exception {
RetryTemplate retryTemplate = new RetryTemplate();
BackOffPolicy backOffPolicy = loadBalancedBackOffPolicyFactory.createBackOffPolicy(this.getClientName());
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
RetryListener[] retryListeners = this.loadBalancedRetryListenerFactory.createRetryListeners(this.getClientName());
if (retryListeners != null && retryListeners.length != 0) {
retryTemplate.setListeners(retryListeners);
}
boolean retryable = isRequestRetryable(request); //HERE
retryTemplate.setRetryPolicy(retryPolicy == null || !retryable ? new NeverRetryPolicy()
: new RetryPolicy(request, retryPolicy, this, this.getClientName()));
return retryTemplate.execute(callback, recoveryCallback);
}
private boolean isRequestRetryable(ContextAwareRequest request) {
return request.getContext() == null ? true :
BooleanUtils.toBooleanDefaultIfNull(request.getContext().getRetryable(), true);
}
在retryableStatusCode()
中,它检查retryableStatusCodes是否包含响应状态代码。
public class RibbonLoadBalancedRetryPolicy implements LoadBalancedRetryPolicy {
public static final IClientConfigKey<String> RETRYABLE_STATUS_CODES = new CommonClientConfigKey<String>("retryableStatusCodes") {};
....
List<Integer> retryableStatusCodes = new ArrayList<>();
public RibbonLoadBalancedRetryPolicy(String serviceId, RibbonLoadBalancerContext context, ServiceInstanceChooser loadBalanceChooser,
IClientConfig clientConfig) {
this.serviceId = serviceId;
this.lbContext = context;
this.loadBalanceChooser = loadBalanceChooser;
String retryableStatusCodesProp = clientConfig.getPropertyAsString(RETRYABLE_STATUS_CODES, "");
String[] retryableStatusCodesArray = retryableStatusCodesProp.split(",");
for(String code : retryableStatusCodesArray) {
if(!StringUtils.isEmpty(code)) {
try {
retryableStatusCodes.add(Integer.valueOf(code.trim()));
} catch (NumberFormatException e) {
//TODO log
}
}
}
}
...
@Override
public boolean retryableStatusCode(int statusCode) {
return retryableStatusCodes.contains(statusCode);
}
}
如果包含,则抛出扩展OkHttpStatusCodeException
的{{1}}。并且RetryableStatusCodeException
捕获了此异常,检查异常是否是RibbonRecoveryCallback
的实现。如果是,则RetryableStatusCodeException
继续重试。或者,抛出不可重试的Expection来中断重试。