在复杂的业务系统中,我们经常遇到需要同时处理多个任务的场景。比如电商平台的订单详情页,需要同时查询订单基本信息、用户信息和商品列表。传统串行执行方式会导致响应时间过长,而简单的多线程开发又面临诸多挑战:
asyncTool框架正是为解决这些问题而生。它基于Spring的异步能力,提供了简洁的任务编排API,让开发者可以像搭积木一样构建复杂的任务执行流程。
首先需要在项目中引入asyncTool的核心依赖:
xml复制<dependency>
<groupId>com.jd.platform</groupId>
<artifactId>asyncTool</artifactId>
<version>1.0.1</version>
</dependency>
这个依赖会引入asyncTool的核心组件,包括任务编排器、工作线程等基础功能。
asyncTool需要依赖线程池来执行任务,我们有两种配置方式:
java复制@Configuration
@EnableAsync
public class TaskExecutePool {
@Autowired
private TaskThreadPoolConfig config; // 线程池配置参数
@Bean
public Executor myTaskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize()); // 核心线程数
executor.setMaxPoolSize(config.getMaxPoolSize()); // 最大线程数
executor.setQueueCapacity(config.getQueueCapacity()); // 队列容量
executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); // 空闲线程存活时间
executor.setThreadNamePrefix("MyExecutor-"); // 线程名前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
executor.initialize();
return executor;
}
}
这种方式的优点是灵活性高,可以完全自定义线程池参数。建议在需要精细控制线程资源的场景使用。
java复制@Configuration
@EnableAsync
public class NativeAsyncTaskExecutePool implements AsyncConfigurer {
@Autowired
private TaskThreadPoolConfig config;
@Override
@Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(config.getCorePoolSize());
executor.setMaxPoolSize(config.getMaxPoolSize());
executor.setQueueCapacity(config.getQueueCapacity());
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
executor.setThreadNamePrefix("MyExecutor2-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, objects) -> {
log.error("异步执行异常:" + ex.getMessage(), ex);
log.error("异常方法:" + method.getName());
};
}
}
这种方式更适合已经使用Spring异步功能的项目,可以统一管理所有异步任务的线程池。
提示:线程池参数需要根据实际业务场景调整。CPU密集型任务建议核心线程数设置为CPU核数+1,IO密集型任务可以设置更大些。
java复制public interface IWorker<T, V> {
/**
* 任务执行逻辑
* @param object 输入参数
* @param allWrappers 所有任务的包装类集合,用于获取其他任务结果
* @return 执行结果
*/
V action(T object, Map<String, WorkerWrapper> allWrappers);
/**
* 任务超时或异常时的默认返回值
*/
V defaultValue();
}
IWorker是任务执行的核心接口,开发者需要实现其中的两个方法:
action(): 包含具体的业务逻辑defaultValue(): 提供任务失败时的默认返回值java复制public interface ICallback<T, V> {
/**
* 任务开始前的回调
*/
void begin();
/**
* 任务执行结束的回调
* @param success 是否成功
* @param param 输入参数
* @param workResult 执行结果
*/
void result(boolean success, T param, WorkResult<V> workResult);
}
ICallback提供了任务生命周期的钩子函数,可以用于监控、日志记录等场景。
WorkerWrapper是任务的包装类,主要属性包括:
| 属性 | 说明 |
|---|---|
id |
任务唯一标识 |
param |
任务输入参数 |
worker |
任务具体实现(IWorker) |
callback |
任务回调实现(ICallback) |
depend |
依赖的前置任务 |
next |
后续任务 |
通过Builder模式可以方便地创建WorkerWrapper实例:
java复制WorkerWrapper wrapper = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(1)
.build();
串行执行是最简单的任务编排方式,适用于有严格先后顺序的任务:
java复制WorkerWrapper wrapperA = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerA")
.worker(new WorkerA())
.callback(new WorkerA())
.param(1)
.build();
WorkerWrapper wrapperB = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerB")
.worker(new WorkerB())
.callback(new WorkerB())
.param(2)
.depend(wrapperA) // B 依赖 A
.build();
WorkerWrapper wrapperC = new WorkerWrapper.Builder<Integer, Integer>()
.id("workerC")
.worker(new WorkerC())
.callback(new WorkerC())
.param(3)
.depend(wrapperB) // C 依赖 B
.build();
// 只需提交第一个任务
Async.beginWork(1000, wrapperA);
对于没有依赖关系的任务,可以并行执行以提高效率:
java复制WorkerWrapper wrapperA = WorkerWrapper.Builder...build();
WorkerWrapper wrapperB = WorkerWrapper.Builder...build();
WorkerWrapper wrapperC = WorkerWrapper.Builder...build();
// 同时提交所有任务
Async.beginWork(1000, wrapperA, wrapperB, wrapperC);
实际业务中经常需要混合串行和并行:
java复制// 先执行A,然后并行执行B和C
WorkerWrapper wrapperA = ...build();
WorkerWrapper wrapperB = new WorkerWrapper.Builder<>()
.depend(wrapperA) // B 依赖 A
...build();
WorkerWrapper wrapperC = new WorkerWrapper.Builder<>()
.depend(wrapperA) // C 依赖 A
...build();
Async.beginWork(1000, wrapperA);
asyncTool提供了完善的异常处理机制:
java复制public class QueryUserWorker implements IWorker<Long, User>, ICallback<Long, User> {
@Override
public User action(Long userId, Map<String, WorkerWrapper> allWrappers) {
try {
return userService.getById(userId);
} catch (Exception e) {
log.error("查询用户异常", e);
return defaultValue();
}
}
@Override
public User defaultValue() {
return new User(); // 返回空对象
}
@Override
public void result(boolean success, Long param, WorkResult<User> workResult) {
if (!success) {
// 记录失败日志或发送告警
monitorService.reportFailure("queryUser", param);
}
}
}
任务未执行:
结果顺序错乱:
性能瓶颈:
让我们通过一个完整的订单详情查询案例,展示asyncTool的实际应用:
java复制@Service
public class OrderService {
public OrderDetail getOrderDetail(Long orderId) {
// 创建订单查询任务
WorkerWrapper<Long, Order> orderWrapper = new WorkerWrapper.Builder<Long, Order>()
.id("orderWorker")
.worker(new QueryOrderWorker())
.callback(new QueryOrderCallback())
.param(orderId)
.build();
// 创建用户查询任务
WorkerWrapper<Long, User> userWrapper = new WorkerWrapper.Builder<Long, User>()
.id("userWorker")
.worker(new QueryUserWorker())
.callback(new QueryUserCallback())
.depend(orderWrapper) // 依赖订单查询
.build();
// 创建商品查询任务
WorkerWrapper<Long, List<Product>> productWrapper = new WorkerWrapper.Builder<Long, List<Product>>()
.id("productWorker")
.worker(new QueryProductsWorker())
.callback(new QueryProductsCallback())
.depend(orderWrapper) // 依赖订单查询
.build();
// 执行所有任务,设置超时时间3秒
List<WorkResult> results = Async.beginWork(3000, orderWrapper);
// 解析结果
OrderDetail detail = new OrderDetail();
detail.setOrder((Order) results.get(0).getResult());
detail.setUser((User) results.get(1).getResult());
detail.setProducts((List<Product>) results.get(2).getResult());
return detail;
}
}
在这个案例中,我们首先查询订单基本信息,然后并行查询用户信息和商品列表。使用asyncTool后,原本需要串行执行的三个查询现在可以并行处理,显著提高了接口响应速度。
在实际项目中,我们通过asyncTool将订单查询接口的响应时间从原来的500ms降低到了200ms左右,效果非常显著。特别是在大促期间,这种优化对系统稳定性的提升更为明显。