feign zipkin Trace Hystrix 重试源码详解
@Configuration
@ConditionalOnProperty(value = "spring.sleuth.enabled", havingValue = "true")
@Slf4j
public class CommonTraceHystrixConfiguration {
/**
* hystrix 超时时间
*/
static int hystrixTimeOut = 5000;
/**
* 请求超时时间
*/
static int requestTimeOut = 4000;
@Bean
public Request.Options options() {
return new Request.Options(requestTimeOut, requestTimeOut);
}
@Bean
Retryer feignRetryer() {
//最多访问一次
return new Retryer.Default(100, SECONDS.toMillis(1), 1);
}
@Bean
@Primary
@Scope("prototype")
public Feign.Builder feignTraceHystrixBuilderExt(BeanFactory beanFactory) {
return HystrixFeign.builder().setterFactory(new SetterFactory() {
public HystrixCommand.Setter create(Target<?> target, Method method) {
String groupKey = target.name();
String commandKey = method.getName();
return HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)).andCommandPropertiesDefaults(
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(hystrixTimeOut)
.withCircuitBreakerSleepWindowInMilliseconds(hystrixTimeOut));
}
}).client(new TraceFeignClient(beanFactory));//TraceFeignClient 是client得一个子类
}
}
org.springframework.cloud.sleuth.instrument.web.client.feign.TraceFeignClient.execute(Request, Options)
@Override
public Response execute(Request request, Request.Options options) throws IOException {
String spanName = getSpanName(request);
Span span = getTracer().createSpan(spanName);
if (log.isDebugEnabled()) {
log.debug("Created new Feign span " + span);
}
try {
AtomicReference<Request> feignRequest = new AtomicReference<>(request);
this.spanInjector.inject(span, feignRequest);
span.logEvent(Span.CLIENT_SEND);
addRequestTags(request);
Request modifiedRequest = feignRequest.get();
if (log.isDebugEnabled()) {
log.debug("The modified request equals " + modifiedRequest);
}
Response response = this.delegate.execute(modifiedRequest, options);
logCr();
return response;
} catch (RuntimeException | IOException e) {
logError(e);
throw e;
} finally {
closeSpan(span);
}
}
feign.SynchronousMethodHandler.invoke(Object[])
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template);//这里会调用TraceFeignClient
} catch (RetryableException e) {
retryer.continueOrPropagate(e);//判断最大重试次输
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
public interface Retryer extends Cloneable {
/**
* if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception.
*/
void continueOrPropagate(RetryableException e);
Retryer clone();
public static class Default implements Retryer {
private final int maxAttempts;
private final long period;
private final long maxPeriod;
int attempt;//当前访问次数
long sleptForMillis;
public Default() {
this(100, SECONDS.toMillis(1), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;//最大访问次输
this.attempt = 1;//当前访问次数
}
// visible for testing;
protected long currentTimeMillis() {
return System.currentTimeMillis();
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
long interval;
if (e.retryAfter() != null) {
interval = e.retryAfter().getTime() - currentTimeMillis();
if (interval > maxPeriod) {
interval = maxPeriod;
}
if (interval < 0) {
return;
}
} else {
interval = nextMaxInterval();
}
try {
Thread.sleep(interval);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
}
sleptForMillis += interval;
}
/**
* Calculates the time interval to a retry attempt. <br> The interval increases exponentially
* with each attempt, at a rate of nextInterval *= 1.5 (where 1.5 is the backoff factor), to the
* maximum interval.
*
* @return time in nanoseconds from now until the next attempt.
*/
long nextMaxInterval() {
long interval = (long) (period * Math.pow(1.5, attempt - 1));
return interval > maxPeriod ? maxPeriod : interval;
}
@Override
public Retryer clone() {
return new Default(period, maxPeriod, maxAttempts);
}
}
/**
* Implementation that never retries request. It propagates the RetryableException.
*/
Retryer NEVER_RETRY = new Retryer() {
@Override
public void continueOrPropagate(RetryableException e) {
throw e;
}
@Override
public Retryer clone() {
return this;
}
};
}