跳到主要内容

【10】Feign 、 Ribbon 重试机制源码分析

文章目录

  • 前言
  • Feign 重试机制
    • Retryer接口
  • Default 类
  • Feign 重试机制源码分析
  • 某些疑问
    • 多次重试请求,发出请求的策略?
    • 其他异常,会被重试吗?
  • Ribbon 重试机制
    • RxJava
  • RetryHandler 接口
  • DefaultLoadBalancerRetryHandler
  • RequestSpecificRetryHandler
  • LoadBalancerCommand
  • 使用案例
  • 总结:

前言

重试机制: 在发生异常时,重新尝试请求,多次还是失败时,才会抛出异常。

应用场景: 可能由于网络抖动出现第一次调用失败,尝试几次就可以恢复正常。

比如Spring提供的声明式的重试类库Spring-Retry

Feign 重试机制

Feign Core 也提供了自己的重试机制,基于Retryer接口。

Retryer接口

Retryer接口位于 feign-core包中,声明了一个连续重试方法,及一个从不重试的实例对象。

public interface Retryer extends Cloneable {


// 不重试Retryer 示例对象,直接抛出异常
Retryer NEVER_RETRY = new Retryer() {


public void continueOrPropagate(RetryableException e) {


throw e;
}

public Retryer clone() {


return this;
}
};
// 连续重试
void continueOrPropagate(RetryableException var1);
}

Default 类

Default 是Retryer 接口的默认实现类,也就是Feign 的默认重试策略。

 public static class Default implements Retryer {


// 最大访问次数(包含了第一次和重试的次数)
private final int maxAttempts;
// 重试的间隔时间
private final long period;
// 最大重试间隔
private final long maxPeriod;
// 当前访问次数
int attempt;
// 总的重试间隔
long sleptForMillis;
// 默认配置(重试间隔100毫秒、最大重试间隔1S、最多访问5次)
public Default() {


this(100L, TimeUnit.SECONDS.toMillis(1L), 5);
}

public Default(long period, long maxPeriod, int maxAttempts) {


this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}

protected long currentTimeMillis() {


return System.currentTimeMillis();
}
// 默认重试算法
public void continueOrPropagate(RetryableException e) {


// 如果重试的次数attempt大于最大重试次数,直接抛出异常
// attempt的初始值为1 ,i++是先进行运算,所以就表示当前重试次数,比较结束了之后再+1,
// 当第attempt 为5时,也就是第五次进入这里,条件为true,则会直接抛出异常,不再进行请求发送。
// 所以最终是,重试了四次,总共请求了5次。
if (this.attempt++ >= this.maxAttempts) {


throw e;
} else {


long interval;
// 响应数据是否包含了 Retry-After 头,这个头用来告诉用户代理需要等待多长时间之后才能继续发送请求
if (e.retryAfter() != null) {


interval = e.retryAfter().getTime() - this.currentTimeMillis();
if (interval > this.maxPeriod) {


interval = this.maxPeriod;
}

if (interval < 0L) {


return;
}
} else {


// 计算重试时间间隔
interval = this.nextMaxInterval();
}

try {


// 线程睡眠时间间隔 第一次为 150ms
Thread.sleep(interval);
} catch (InterruptedException var5) {


Thread.currentThread().interrupt();
throw e;
}
// 记录时间间隔总数
this.sleptForMillis += interval;
}
}
// 计算公式=》 配置的间隔时间(默认100ms)* (1.5 的(当前重试次数)次方)
// 接着判断 当前时间时间,是否超出了最大限制,超过了则返回最大时间间隔。
long nextMaxInterval() {


long interval = (long)((double)this.period * Math.pow(1.5D, (double)(this.attempt - 1)));
return interval > this.maxPeriod ? this.maxPeriod : interval;
}

public Retryer clone() {


return new Retryer.Default(this.period, this.maxPeriod, this.maxAttempts);
}
}