参考:https://www.jianshu.com/p/0a6ee8c13522
指数退避的原理是对于连续错误响应,重试等待间隔越来越长。
您应该实施最长延迟间隔和最大重试次数。最长延迟间隔和最大重试次数不一定是固定值,并且应当根据正在执行的操作和其他本地因素(例如网络延迟)进行设置。
大多数指数退避算法会利用抖动(随机延迟)来防止连续的冲突。
由于在这些情况下您并未尝试避免此类冲突,因此无需使用此随机数字。但是,如果使用并发客户端,抖动可帮助您更快地成功执行请求
1 配置异步线程池
package cn.shu.zhishutuibi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class BeanConfig {
/*配置一个线程池*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(4);
// 设置最大线程数
executor.setMaxPoolSize(4);
// 设置队列容量
executor.setQueueCapacity(4);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("itsm-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
2 写一个Conreoller
@GetMapping("/asynctest")
public String asynctest() {
System.out.println("asynctest方法开始"+new Date());
task.doTaskOne();
System.out.println("asynctest方法结束"+new Date());
return "访问成功";
}
3写一个任务类
package cn.shu.zhishutuibi;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.*;
/*
* 配置任务*/
@Component
public class Task {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Task.class);
private static Random random = new Random();
//最大重试次数
public static int MAX_RETRIES = 4;
//最大重试间隔 5分钟
public static long MAX_WAIT_INTERVAL = 1000L * 60 * 5;
/* @Async
public void doTaskOne() throws Exception {
System.out.println("开始做任务一");
long start = System.currentTimeMillis();
Thread.sleep(random.nextInt(10000));
long end = System.currentTimeMillis();
System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
}*/
@Async
public void doTaskOne() {
System.out.println(Thread.currentThread().getName() + "开始做任务一");
logger.info(Thread.currentThread().getName() + "开始做任务一");
long start = System.currentTimeMillis();
// Do some asynchronous operation.
// long token = asyncOperation();
int retries = 0;
boolean retry = false;
System.out.println("开始执行getAsyncOperationResult");
logger.info("开始执行getAsyncOperationResult");
Results result = getAsyncOperationResult();
System.out.println("结束执行getAsyncOperationResult:" + result);
logger.info("结束执行getAsyncOperationResult:" + result);
if (Results.Retry == result) {
try {
Thread.sleep(1000L * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
do {
long waitTime = Math.min(getWaitTimeExp(retries), MAX_WAIT_INTERVAL);
System.out.print(waitTime + "\n");
// Wait for the result. 单位是毫秒
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Get the result of the asynchronous operation.
logger.info("第"+retries+"次请求 时间:"+new Date());
Results result1 = getAsyncOperationResult();
if (Results.SUCCESS == result1) {
retry = false;
} else if (Results.Retry == result1) {
retry = true;
} else {
// Some other error occurred, so stop calling the API.
retry = false;
}
} while (retry && (retries++ < MAX_RETRIES));
}
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "完成任务一,耗时:" + (end - start) + "毫秒");
logger.info(Thread.currentThread().getName() + "完成任务一,耗时:" + (end - start) + "毫秒");
}
private Results getAsyncOperationResult() {
System.out.println("do somethings");
return Results.Retry;
}
/*
* Returns the next wait interval, in milliseconds, using an exponential
* backoff algorithm.
*/
public static long getWaitTimeExp(int retryCount) {
long waitTime = ((long) Math.pow(2, retryCount) * 1000L * 30);
return waitTime;
}
public enum Results {
SUCCESS,
Retry
}
}