spring-retry、guava的retry都提供有重试工具,但二者均存在一个确缺点,即如果重试等待过程中会一直阻塞工作线程,这对于在生产环境使用是存在风险的,如果存在大量长时间等待的重试任务将会耗尽系统线程资源,下文基于线程池来完成一个简易的重试工具类。
核心思想
将任务封装为一个task,将任务的重试放入可调度的线程池中完成执行,避免在重试间隔中,线程陷入无意义的等待,同时将重试机制抽象为重试策略。
代码实现
重试工具类
package com.huakai.springenv.retry.v2;
import lombok.extern.slf4j.slf4j;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import java.util.concurrent.scheduledexecutorservice;
import java.util.concurrent.timeunit;
import java.util.concurrent.atomic.atomicinteger;
import java.util.function.function;
@slf4j
public class retryutil {
public static executorservice executor = executors.newfixedthreadpool(1);
private static final scheduledexecutorservice scheduler_executor = executors.newscheduledthreadpool(20);
/**
* 任务重试
* @param actualtaskfunction 执行的任务函数
* @param resulthandler 任务结果处理器
* @param maxretry 最大重试次数
* @param retrystrategy 重试策略
*/
public static void retrytask(
function<integer, string> actualtaskfunction,
function<string, boolean> resulthandler,
int maxretry,
retrystrategy retrystrategy // 使用策略模式
) {
runnable runnable = new runnable() {
final atomicinteger retrycount = new atomicinteger(); // 当前重试次数
final atomicinteger maxretrycount = new atomicinteger(maxretry); // 最大重试次数
@override
public void run() {
string taskresult = actualtaskfunction.apply(retrycount.get()); // 执行任务
boolean tasksuccess = resulthandler.apply(taskresult); // 处理任务结果
if (tasksuccess) {
if (retrycount.get() > 1) {
log.info("任务重试成功,重试次数:{}", retrycount.get());
}
return; // 任务成功,不需要再重试
}
if (retrycount.incrementandget() == maxretrycount.get()) {
log.warn("任务重试失败,重试次数:{}", retrycount.get());
return; // 达到最大重试次数,停止重试
}
// 获取重试间隔
long delay = retrystrategy.getdelay(retrycount.get());
timeunit timeunit = retrystrategy.gettimeunit(retrycount.get());
// 安排下次重试
scheduler_executor.schedule(this, delay, timeunit);
log.info("任务重试失败,等待 {} {} 后再次尝试,当前重试次数:{}", delay, timeunit, retrycount.get());
}
};
executor.execute(runnable); // 执行任务
}
public static void main(string[] args) {
// 使用指数退避重试策略
retrystrategy retrystrategy = new exponentialbackoffretrystrategy(1, timeunit.seconds);
retrytask(
retrycount -> "task result",
taskresult -> math.random() < 0.1,
5,
retrystrategy
);
}
}
重试策略
指数退避
package com.huakai.springenv.retry.v2;
import java.util.concurrent.timeunit;
/**
* 指数退避重试策略
*/
public class exponentialbackoffretrystrategy implements retrystrategy {
private final long initialdelay;
private final timeunit timeunit;
public exponentialbackoffretrystrategy(long initialdelay, timeunit timeunit) {
this.initialdelay = initialdelay;
this.timeunit = timeunit;
}
@override
public long getdelay(int retrycount) {
return (long) (initialdelay * math.pow(2, retrycount - 1)); // 指数退避
}
@override
public timeunit gettimeunit(int retrycount) {
return timeunit;
}
}
自定义重试间隔时间
package com.huakai.springenv.retry.v2;
import java.util.list;
import java.util.concurrent.timeunit;
/**
* 自定义重试间隔时间的重试策略
*/
public class customerintervalretrystrategy implements retrystrategy {
// 配置重试间隔和时间单位
list<retryinterval> retryintervals;
public customerintervalretrystrategy(list<retryinterval> retryintervals) {
this.retryintervals = retryintervals;
}
@override
public long getdelay(int retrycount) {
return retryintervals.get(retrycount).getdelay();
}
@override
public timeunit gettimeunit(int retrycount){
return retryintervals.get(retrycount).gettimeunit();
}
}
固定间隔
package com.huakai.springenv.retry.v2;
import java.util.concurrent.timeunit;
/**
* 固定间隔重试策略
*/
public class fixedintervalretrystrategy implements retrystrategy {
private final long interval;
private final timeunit timeunit;
public fixedintervalretrystrategy(long interval, timeunit timeunit) {
this.interval = interval;
this.timeunit = timeunit;
}
@override
public long getdelay(int retrycount) {
return interval;
}
@override
public timeunit gettimeunit(int retrycount) {
return timeunit;
}
}
到此这篇关于java实现自定义重试工具类的文章就介绍到这了,更多相关java重试工具类内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论