具有完全抖动 Java 实现的指数退避算法
Exponential Backoff Algorithm with Full Jitter Java Implementation
我正在尝试在纯 Java 中创建一个实用程序 class,它将包含具有完整抖动的指数退避算法实现所需的逻辑,因为将有多个客户端发送请求。
我有另一个 class 方法,它执行 GET 或 POST 请求和 returns 带有状态代码的响应。仅当状态代码在 5xx 中时,我才想重试(又名使用指数退避策略)。当前代码未编译。
调用方法如下所示:
HttpResponse response = executeGetRequest( params );
int statusCode = response.getStatusCode();
//some status code validation
我的 ExponentialBackoffStrategy class 是:
public class ExponentialBackoffStrategy {
private final long maxBackoff;
private long backoffValue;
private long attempts;
private static final long DEFAULT_MAX_RETRIES = 900_000;
private Random random = new Random();
public ExponentialBackoffStrategy( long maxBackoff ) {
this.maxBackoff = maxBackoff;
}
public long getWaitTimeExp() {
if( backoffValue >= maxBackoff ) {
return maxBackoff;
}
double pow = Math.pow( 2, attempts++ );
int rand = random.nextInt( 1000 );
backoffValue = ( long ) Math.min( pow + rand, maxBackoff );
return backoffValue;
}
public static ExponentialBackoffStrategy getDefault() {
return new ExponentialBackoffStrategy( DEFAULT_MAX_RETRIES );
}
}
我想获得一些关于已实施 class 的反馈,关于我是否可以做得更好以及如何将其与调用方方法集成。我现在的想法是:
ExponentialBackoffStrategy backoff = ExponentialBackoffStrategy.getDefault();
boolean retry = false;
HttpResponse response = null;
int statusCode = 0;
do {
response = executeGetRequest( params );
statusCode = response.getStatusLine().getStatusCode();
if( statusCode >= 500 && statusCode < 600 ) {
retry = true;
try {
Thread.sleep( backoff.getWaitTimeExp() );
} catch ( InterruptedException e ) {
//handle exception
}
}
} while ( retry );
如有任何帮助,我们将不胜感激!
编辑:
响应实际上位于 try with resources.
try ( HttpResponse response = backoff.attempt(
() -> executeGetRequest( params ),
r -> {
final int statusCode = response.getStatusLine().getStatusCode();
return statusCode < 500 || statusCode >= 600;
}
);)
我 运行 有两个问题:
- 在线
final int statusCode = response.getStatusLine().getStatusCode();
“响应”带有红色下划线“变量 'response' 可能尚未初始化”。试图将它带到 try 块之外并尝试使用不喜欢的资源。
- executeGetRequest 现在需要 lambda 内部的 catch 块:
try ( HttpResponse response = executePostRequest( params ) ) {
您可以将更多样板文件放入 class,例如:
public class ExponentialBackoffStrategy {
...
@Nullable
public <T> T attempt(Supplier<T> action, Predicate<T> success) {
int attempts = 0;
T result = action.get();
while (!success.test(result)) {
try {
Thread.sleep(getWaitTimeExp(attempts++));
} catch ( InterruptedException e ) {
//handle exception
}
result = action.get();
}
return result;
}
}
然后你会像这样使用它:
ExponentialBackoffStrategy backoff = ExponentialBackoffStrategy.getDefault();
final HttpResponse response = backoff.attempt(
() -> executeGetRequest( params ),
r -> {
final int statusCode = r.getStatusLine().getStatusCode();
return statusCode < 500 || statusCode >= 600;
}
);
这样可以减少你程序中的重复代码量,重试逻辑可以测试一次。
我已将可变状态(attempts
和 backoffValue
可以删除)从 class 移到 attempt()
函数中的局部变量中。这意味着单个 ExponentialBackoffStrategy
实例可以安全地重用,也可以被多个线程使用。所以 getWaitTimeExp
变成了一个没有副作用的函数:
private long getWaitTimeExp(int attempts) {
final double pow = Math.pow( 2, attempts);
final int rand = random.nextInt( 1000 );
return ( long ) Math.min( pow + rand, maxBackoff );
}
这是未经测试的代码!
您也应该在重试一定次数后停止重试。
来测试一下你。想将休眠和随机数生成放入单独的组件中,然后注入 ExponentialBackoffStrategy
。您的静态工厂方法可以注入生产实现,您的测试将使用 ExponentialBackoffStrategy
构造函数并传递模拟。
所以你会有接口:
interface RandomNumber {
int next();
}
interface Sleeper {
void sleep(long milliseconds);
}
和一个构造函数:
protected ExponentialBackoffStrategy(long maxBackoff, RandomNumber randomNumber, Sleeper sleeper) {
...
}
上面的是指数退避,但没有随机休眠时间。我试过给随机时间。
如果这个逻辑看起来不错并且我想要最大间隔时间范围。
if tell mx_interal as 2000L - 那么第一次尝试应该是 2000L,如果计算应该落在 1 > < 2000L 之间,则第二次尝试。使它始终在这个范围内。
private long getWaitTimeExp(int retries) {
return getWaitTimeExp(retries, withDelayBetweenTries(maxWaitInterval, ChronoUnit.MILLIS)).toMillis();
}
public Duration getWaitTimeExp(int numberOfTriesFailed, Duration delayBetweenAttempts) {
int i = ThreadLocalRandom.current().nextInt(1, this.maxMultiplier);
return getDurationToWait(numberOfTriesFailed, Duration.ofMillis(i * delayBetweenAttempts.toMillis()));
}
public Duration getDurationToWait(int numberOfTriesFailed, Duration delayBetweenAttempts) {
double exponentialMultiplier = Math.pow(2.0, numberOfTriesFailed);
double result = exponentialMultiplier * delayBetweenAttempts.toMillis();
long millisToWait = (long) Math.min(result, Long.MAX_VALUE);
return Duration.ofMillis(millisToWait);
}
public static Duration withDelayBetweenTries(long amount, ChronoUnit time) {
return Duration.of(amount, time);
}
我正在尝试在纯 Java 中创建一个实用程序 class,它将包含具有完整抖动的指数退避算法实现所需的逻辑,因为将有多个客户端发送请求。 我有另一个 class 方法,它执行 GET 或 POST 请求和 returns 带有状态代码的响应。仅当状态代码在 5xx 中时,我才想重试(又名使用指数退避策略)。当前代码未编译。
调用方法如下所示:
HttpResponse response = executeGetRequest( params );
int statusCode = response.getStatusCode();
//some status code validation
我的 ExponentialBackoffStrategy class 是:
public class ExponentialBackoffStrategy {
private final long maxBackoff;
private long backoffValue;
private long attempts;
private static final long DEFAULT_MAX_RETRIES = 900_000;
private Random random = new Random();
public ExponentialBackoffStrategy( long maxBackoff ) {
this.maxBackoff = maxBackoff;
}
public long getWaitTimeExp() {
if( backoffValue >= maxBackoff ) {
return maxBackoff;
}
double pow = Math.pow( 2, attempts++ );
int rand = random.nextInt( 1000 );
backoffValue = ( long ) Math.min( pow + rand, maxBackoff );
return backoffValue;
}
public static ExponentialBackoffStrategy getDefault() {
return new ExponentialBackoffStrategy( DEFAULT_MAX_RETRIES );
}
}
我想获得一些关于已实施 class 的反馈,关于我是否可以做得更好以及如何将其与调用方方法集成。我现在的想法是:
ExponentialBackoffStrategy backoff = ExponentialBackoffStrategy.getDefault();
boolean retry = false;
HttpResponse response = null;
int statusCode = 0;
do {
response = executeGetRequest( params );
statusCode = response.getStatusLine().getStatusCode();
if( statusCode >= 500 && statusCode < 600 ) {
retry = true;
try {
Thread.sleep( backoff.getWaitTimeExp() );
} catch ( InterruptedException e ) {
//handle exception
}
}
} while ( retry );
如有任何帮助,我们将不胜感激!
编辑: 响应实际上位于 try with resources.
try ( HttpResponse response = backoff.attempt(
() -> executeGetRequest( params ),
r -> {
final int statusCode = response.getStatusLine().getStatusCode();
return statusCode < 500 || statusCode >= 600;
}
);)
我 运行 有两个问题:
- 在线
final int statusCode = response.getStatusLine().getStatusCode();
“响应”带有红色下划线“变量 'response' 可能尚未初始化”。试图将它带到 try 块之外并尝试使用不喜欢的资源。 - executeGetRequest 现在需要 lambda 内部的 catch 块:
try ( HttpResponse response = executePostRequest( params ) ) {
您可以将更多样板文件放入 class,例如:
public class ExponentialBackoffStrategy {
...
@Nullable
public <T> T attempt(Supplier<T> action, Predicate<T> success) {
int attempts = 0;
T result = action.get();
while (!success.test(result)) {
try {
Thread.sleep(getWaitTimeExp(attempts++));
} catch ( InterruptedException e ) {
//handle exception
}
result = action.get();
}
return result;
}
}
然后你会像这样使用它:
ExponentialBackoffStrategy backoff = ExponentialBackoffStrategy.getDefault();
final HttpResponse response = backoff.attempt(
() -> executeGetRequest( params ),
r -> {
final int statusCode = r.getStatusLine().getStatusCode();
return statusCode < 500 || statusCode >= 600;
}
);
这样可以减少你程序中的重复代码量,重试逻辑可以测试一次。
我已将可变状态(attempts
和 backoffValue
可以删除)从 class 移到 attempt()
函数中的局部变量中。这意味着单个 ExponentialBackoffStrategy
实例可以安全地重用,也可以被多个线程使用。所以 getWaitTimeExp
变成了一个没有副作用的函数:
private long getWaitTimeExp(int attempts) {
final double pow = Math.pow( 2, attempts);
final int rand = random.nextInt( 1000 );
return ( long ) Math.min( pow + rand, maxBackoff );
}
这是未经测试的代码!
您也应该在重试一定次数后停止重试。
来测试一下你。想将休眠和随机数生成放入单独的组件中,然后注入 ExponentialBackoffStrategy
。您的静态工厂方法可以注入生产实现,您的测试将使用 ExponentialBackoffStrategy
构造函数并传递模拟。
所以你会有接口:
interface RandomNumber {
int next();
}
interface Sleeper {
void sleep(long milliseconds);
}
和一个构造函数:
protected ExponentialBackoffStrategy(long maxBackoff, RandomNumber randomNumber, Sleeper sleeper) {
...
}
上面的是指数退避,但没有随机休眠时间。我试过给随机时间。
如果这个逻辑看起来不错并且我想要最大间隔时间范围。
if tell mx_interal as 2000L - 那么第一次尝试应该是 2000L,如果计算应该落在 1 > < 2000L 之间,则第二次尝试。使它始终在这个范围内。
private long getWaitTimeExp(int retries) {
return getWaitTimeExp(retries, withDelayBetweenTries(maxWaitInterval, ChronoUnit.MILLIS)).toMillis();
}
public Duration getWaitTimeExp(int numberOfTriesFailed, Duration delayBetweenAttempts) {
int i = ThreadLocalRandom.current().nextInt(1, this.maxMultiplier);
return getDurationToWait(numberOfTriesFailed, Duration.ofMillis(i * delayBetweenAttempts.toMillis()));
}
public Duration getDurationToWait(int numberOfTriesFailed, Duration delayBetweenAttempts) {
double exponentialMultiplier = Math.pow(2.0, numberOfTriesFailed);
double result = exponentialMultiplier * delayBetweenAttempts.toMillis();
long millisToWait = (long) Math.min(result, Long.MAX_VALUE);
return Duration.ofMillis(millisToWait);
}
public static Duration withDelayBetweenTries(long amount, ChronoUnit time) {
return Duration.of(amount, time);
}