使用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 loadBalancerFactory,
LoadBalancerRetryPolicy.Factory retryPolicyFactory,
ObjectProvider> 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 loadBalancerFactory) {
return new RetryableExchangeFilterFunctionLoadBalancerRetryPolicy.Factory(loadBalancerFactory);
}
当发起http 请求时就会执行org.springframework.cloud.client.loadbalancer.reactive.RetryableLoadBalancerExchangeFilterFunction#filter 方法中
@Override
public Mono 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 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 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(
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(
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);
}
参数详细解:
默认情况下,Ribbon不会针对HTTP非正常响应状态值(如404、502等)进行重试。如果您需要对特殊的Http状态进行重试,需要配置该参数。
这个参数指的是是否允许所有的HTTP请求(GET,POST,PUT等)重试。默认值是false,只允许GET请求重试。对于POST等请求,请慎重使用。
这个参数用于配置当前实例最大重试次数,默认值为0。重试次数不包括第一次请求。
这个参数指的是切换实例最大重试次数,默认值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
添加依赖
org.springframework.retry
spring-retry
1.3.1
添加配置
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://example.com
predicates:
- Path=/my/path/**
filters:
- RewritePath=/my/path/(?.*), /$\{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倍。