Java實(shí)現(xiàn)自定義重試工具類
Spring-retry、guava的Retry都提供有重試工具,但二者均存在一個(gè)確缺點(diǎn),即如果重試等待過(guò)程中會(huì)一直阻塞工作線程,這對(duì)于在生產(chǎn)環(huán)境使用是存在風(fēng)險(xiǎn)的,如果存在大量長(zhǎng)時(shí)間等待的重試任務(wù)將會(huì)耗盡系統(tǒng)線程資源,下文基于線程池來(lái)完成一個(gè)簡(jiǎn)易的重試工具類。
核心思想
將任務(wù)封裝為一個(gè)task,將任務(wù)的重試放入可調(diào)度的線程池中完成執(zhí)行,避免在重試間隔中,線程陷入無(wú)意義的等待,同時(shí)將重試機(jī)制抽象為重試策略。
代碼實(shí)現(xiàn)
重試工具類
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);
/**
* 任務(wù)重試
* @param actualTaskFunction 執(zhí)行的任務(wù)函數(shù)
* @param resultHandler 任務(wù)結(jié)果處理器
* @param maxRetry 最大重試次數(shù)
* @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(); // 當(dāng)前重試次數(shù)
final AtomicInteger maxRetryCount = new AtomicInteger(maxRetry); // 最大重試次數(shù)
@Override
public void run() {
String taskResult = actualTaskFunction.apply(retryCount.get()); // 執(zhí)行任務(wù)
Boolean taskSuccess = resultHandler.apply(taskResult); // 處理任務(wù)結(jié)果
if (taskSuccess) {
if (retryCount.get() > 1) {
log.info("任務(wù)重試成功,重試次數(shù):{}", retryCount.get());
}
return; // 任務(wù)成功,不需要再重試
}
if (retryCount.incrementAndGet() == maxRetryCount.get()) {
log.warn("任務(wù)重試失敗,重試次數(shù):{}", retryCount.get());
return; // 達(dá)到最大重試次數(shù),停止重試
}
// 獲取重試間隔
long delay = retryStrategy.getDelay(retryCount.get());
TimeUnit timeUnit = retryStrategy.getTimeUnit(retryCount.get());
// 安排下次重試
SCHEDULER_EXECUTOR.schedule(this, delay, timeUnit);
log.info("任務(wù)重試失敗,等待 {} {} 后再次嘗試,當(dāng)前重試次數(shù):{}", delay, timeUnit, retryCount.get());
}
};
EXECUTOR.execute(runnable); // 執(zhí)行任務(wù)
}
public static void main(String[] args) {
// 使用指數(shù)退避重試策略
RetryStrategy retryStrategy = new ExponentialBackoffRetryStrategy(1, TimeUnit.SECONDS);
retryTask(
retryCount -> "task result",
taskResult -> Math.random() < 0.1,
5,
retryStrategy
);
}
}
重試策略
指數(shù)退避
package com.huakai.springenv.retry.v2;
import java.util.concurrent.TimeUnit;
/**
* 指數(shù)退避重試策略
*/
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)); // 指數(shù)退避
}
@Override
public TimeUnit getTimeUnit(int retryCount) {
return timeUnit;
}
}
自定義重試間隔時(shí)間
package com.huakai.springenv.retry.v2;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 自定義重試間隔時(shí)間的重試策略
*/
public class CustomerIntervalRetryStrategy implements RetryStrategy {
// 配置重試間隔和時(shí)間單位
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;
}
}
到此這篇關(guān)于Java實(shí)現(xiàn)自定義重試工具類的文章就介紹到這了,更多相關(guān)Java重試工具類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之HashMap和HashSet
這篇文章主要介紹了HashMap和HashSet,什么是哈希表以及HashMap的部分源碼解讀,想了解更多的小伙伴,可以參考閱讀本文2023-03-03
Spring AOP實(shí)現(xiàn)接口請(qǐng)求記錄到數(shù)據(jù)庫(kù)的示例代碼
這篇文章主要介紹了Spring AOP實(shí)現(xiàn)接口請(qǐng)求記錄到數(shù)據(jù)庫(kù),代碼包括引入AOP依賴及創(chuàng)建日志記錄表,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
SpringBoot 并發(fā)登錄人數(shù)控制的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot 并發(fā)登錄人數(shù)控制的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
SpringBoot訪問(wèn)web中的靜態(tài)資源的方式小結(jié)
這篇文章主要介紹了SpringBoot訪問(wèn)web中的靜態(tài)資源的方式,本文給大家介紹了兩種方式,通過(guò)代碼示例和圖文講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-10-10
四步輕松搞定java web每天定時(shí)執(zhí)行任務(wù)
本篇文章主要介紹了四步輕松搞定java web每天定時(shí)執(zhí)行任務(wù),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
springboot加載復(fù)雜的yml文件獲取不到值的解決方案
這篇文章主要介紹了springboot加載復(fù)雜的yml文件獲取不到值的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
java Spring Boot 配置redis pom文件操作
這篇文章主要介紹了java Spring Boot 配置redis pom文件操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
關(guān)于feign接口動(dòng)態(tài)代理源碼解析
這篇文章主要介紹了關(guān)于feign接口動(dòng)態(tài)代理源碼解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

