1. 实现原理
一图胜千言
大致的实现原理就是当请求发生的时候,会往时间轮里面添加一个延时任务,至于延时时长就是任务的超时阈值,当任务在设置的超时阈值内还没执行完成,就会触发延迟任务逻辑。延迟任务逻辑就是会 set 此次请求超时后的默认值。所以我们需要将任务包装成一个 futureTask。
前置知识:
- 时间轮:Kafka延迟任务时间轮解析 + java版源码 - 掘金 (juejin.cn)
- futureTask底层原理和futureTask增强:FutureTask源码解析,以及增强适配 - 掘金 (juejin.cn)
2. 快速入门
/**
* 测试异步不超时
*/
public static void testNormalAsync() {
System.out.print("开始执行, 时间 : ");
printCurTime();
RequestExecutor.doRequestAsync(
() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "正常返回结果";
},
(res, e) -> {
System.out.print("触发回调逻辑, 时间 : ");
printCurTime();
System.out.println(res);
},
2,
TimeUnit.SECONDS,
"默认返回结果"
);
//开始执行, 时间 : 2024-08-13 00:26:54
//触发回调逻辑, 时间 : 2024-08-13 00:26:55
//正常返回结果
}
/**
* 测试异步超时
*/
public static void testTimeOutAsync() {
System.out.print("开始执行, 时间 : ");
printCurTime();
RequestExecutor.doRequestAsync(
() -> {
// 模拟耗时操作 2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "正常返回结果";
},
(res, e) -> {
System.out.print("触发回调逻辑, 时间 : ");
printCurTime();
System.out.println(res);
},
1,
TimeUnit.SECONDS,
"默认返回结果"
);
// 开始执行, 时间 : 2024-08-13 00:27:30
// 触发回调逻辑, 时间 : 2024-08-13 00:27:31
// 默认返回结果
}
/**
* 测试同步超时
*/
public static void testTimeOutSync() {
System.out.print("开始执行, 时间 : ");
printCurTime();
String res = RequestExecutor.doRequestSync(
() -> {
// 模拟耗时操作 2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "正常返回结果";
},
1,
TimeUnit.SECONDS,
"默认返回结果"
);
System.out.print("结果 :" + res + ", 时间 :");
printCurTime();
// 开始执行, 时间 : 2024-08-13 00:32:27
// 结果 :默认返回结果, 时间 :2024-08-13 00:32:28
}
/**
* 测试同步正常
*/
public static void testNormalSync() {
System.out.print("开始执行, 时间 : ");
printCurTime();
String res = RequestExecutor.doRequestSync(
() -> {
return "正常返回结果";
},
2,
TimeUnit.SECONDS,
"默认返回结果"
);
System.out.print("结果 :" + res + ", 时间 :");
printCurTime();
// 开始执行, 时间 : 2024-08-13 00:32:02
// 结果 :正常返回结果, 时间 :2024-08-13 00:32:02
}
3. 源码讲解
package com.hdu;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
* 请求执行器 , 可以给请求设置超时时间 如果超时未完成 那么返回设置的默认结果
*/
public class RequestExecutor {
private static final SystemTimer timer = TimerFactory.createSystemTimer(
"doRequest",
200,
TimeUnit.MILLISECONDS,
5
);
private static ExecutorService executorService = Executors.newFixedThreadPool(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "doRequest");
}
});
/**
* 同步调用
*
* @param supplier 执行逻辑
* @param timeout 超时时间
* @param timeUnit 超时时间单位
* @param defaultResult 默认返回结果
* @param <T>
* @return
*/
public static <T> T doRequestSync(Supplier<T> supplier,
long timeout, TimeUnit timeUnit,
T defaultResult) {
// 1. 包装原始执行逻辑 + 添加回调逻辑
EnhanceFutureTask<T> enhanceFutureTask = EnhanceFutureTask.of(
() -> {
try {
return supplier.get();
} catch (Exception e) {
return defaultResult;
}
}
);
// 2. 提交延迟任务到时间轮, 超时返回默认返回结果
TimerTask timerTask = new TimerTask(timeout, timeUnit) {
@Override
public void run() {
enhanceFutureTask.set(defaultResult);
}
};
timer.add(timerTask);
try {
executorService.submit(enhanceFutureTask);
return enhanceFutureTask.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
/**
* 异步调用
*
* @param supplier 执行逻辑
* @param callback 回调逻辑
* @param timeout 超时时间
* @param timeUnit 超时时间单位
* @param defaultResult 默认返回结果
* @param <T>
*/
public static <T> void doRequestAsync(Supplier<T> supplier,
BiConsumer<T, Throwable> callback,
long timeout, TimeUnit timeUnit,
T defaultResult) {
// 1. 包装原始执行逻辑 + 添加回调逻辑
EnhanceFutureTask<T> enhanceFutureTask = EnhanceFutureTask
.of(
() -> {
try {
return supplier.get();
} catch (Exception e) {
throw e;
}
}
).addFinallyListener(
(futureTask) -> {
callback.accept(futureTask.getResult(), futureTask.getException());
}
);
// 2. 提交延迟任务到时间轮, 超时返回默认返回结果
timer.add(
new TimerTask(timeout, timeUnit) {
@Override
public void run() {
enhanceFutureTask.set(defaultResult);
}
}
);
executorService.submit(enhanceFutureTask);
}
}
4. 源码
interface_timeout_control: interface_timeout_control (gitee.com)