问题描述
现在在项目中碰到一个情况,导出数据到excel,在数据量比较下的时候直接下载,在数据量比较大时保存到服务的文件列表,后续再供用户下载。
也就是需要避免前端因后端处理时间过长而提示超时的问题。
问题处理
步骤:
- 主线程开启线程1进行数据读取和转换byte数组,处理结束唤醒主线程
- 开启线程2进行计时,到时间后唤醒主线程
- 主线程阻塞,等待唤醒
- 判断是被哪个线程唤醒的,如果是线程1,直接返回数据,请求结束;如果是线程2,则表示读取转换未完成,需要转换为异步处理,这时直接结束请求,返回提示信息。
计划:
- 使用包括主线程在内的3个线程
- 使用CountDownLatch进行主线程唤醒
代码实现
伪代码如下:
ThreadPoolTaskExecutor executor;
byte[] handle() throws InterruptedException, ExecutionException {
// 计数器为 1,无论哪个线程计数,都会唤醒主线程
CountDownLatch latch = new CountDownLatch(1);
// 记录 是否数据处理完成
AtomicBoolean flag = new AtomicBoolean(false);
// 数据读取线程
Future<List<Map<String, Object>>> future = executor.submit(() -> {
synchronized (flag){
flag.set(true);
latch.countDown();
}
return readSomething();
});
executor.execute(() -> {
try {
Thread.sleep(5 * 1000);
}catch (Exception ignored){}finally {
latch.countDown();
}
});
if (!flag.get()) {
latch.await();
}
if (flag.get()) {
// 数据读取完成
List<Map<String, Object>> maps = future.get();
// 返回下载数据
return toExcelByte(maps);
}else {
// 数据读取未完成,需要转异步,并返回响应
executor.execute(() -> {
List<Map<String, Object>> maps;
try {
maps = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
toSaveLocal(toExcelByte(maps));
});
return "当前数据处理时间较长,请稍后在文件列表中下载".getBytes();
}
}
List<Map<String, Object>> readSomething() {
return Collections.EMPTY_LIST;
}
byte[] toExcelByte(List<Map<String, Object>> maps) {
// 数据保存到excel byte
return new byte[]{};
}
void toSaveLocal(byte[] bytes) {
// 写到本地服务器,或者文件服务器,以供下载
}