项目场景:
系统通过原始数据计算生成出用于后续数据解析的校准用的算法函数,这一步骤采用异步执行,将计算逻辑丢到线程池里,主线程(请求接口)调用完后将计算状态修改未"进行中"。
当算法计算完成后再修改本条算法计算状态为"已完成"。
问题描述:
以上流程是我们期望的结果,但是在整个计算过程中不可避免产生异常(因为原始数据不可控,可能会有脏数据),或者因为原始数据过大导致计算非常耗时导致线程得不到释放一直被占用。这些都是潜在的问题。
//这里用枚举定义状态,列出了三种状态
COMPLETED(1, "已完成"),
TRAINING(2, "训练中"),
TRAINING_FAILED(3, "训练失败");
解决方案:
通过Future.get(long timeout, TimeUnit unit)方法处理超时情况
1.首先定义线程池以及其生命周期
//线程池定义声明
private static final BlockingQueue<Runnable> workQueue1 = new LinkedBlockingQueue<>(20);
private static final BlockingQueue<Runnable> workQueue2 = new LinkedBlockingQueue<>(20);
//任务线程池
private static final ExecutorService executorService = new ThreadPoolExecutor(1, 4, 30, TimeUnit.SECONDS, workQueue1);
//超时检查线程池
private static final ExecutorService checkService = new ThreadPoolExecutor(1, 4, 30, TimeUnit.SECONDS, workQueue2);
//超时时间
private static final long OVERTIME = 60;
//销毁线程池
@PreDestroy
public void destroy() {
shutdownExecutorService(executorService);
shutdownExecutorService(checkService);
}
public static void shutdownExecutorService(ExecutorService pool) {
if (pool != null && !pool.isShutdown()) {
pool.shutdown();
try {
if (!pool.awaitTermination(OVERTIME, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(OVERTIME, TimeUnit.SECONDS)) {
log.info("终止失败");
}
}
} catch (InterruptedException ie) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
2.编写代码
这一行查原始数据
List<Map<String, Object>> dataList = tCalibrationAlgorithmMapper.findRegressionLine(trainingParmsEntity);
这一段生成算法
calibrationAlgorithm = AlgorithmTrainingUtils.training(trainingParmsEntity, dataList);
然后捕获这一块代码进行异常处理,如果有异常则打印日志并set训练状态为failed
然后持久化数据,将此任务提交到线程池。然后再调用timeoutCheck()方法,在checkServer线程池里执行检查任务,这里通过get(long timeout, TimeUnit unit)进行超时检查。捕获TimeoutException 超时异常,进行后续处理。
public Future<Void> submitTask(String entityId, TrainingParmsEntity trainingParmsEntity) {
Callable<Void> task = () -> {
TCalibrationAlgorithm calibrationAlgorithm = new TCalibrationAlgorithm();
try {
log.info("计算中...");
List<Map<String, Object>> dataList = tCalibrationAlgorithmMapper.findRegressionLine(trainingParmsEntity);
calibrationAlgorithm = AlgorithmTrainingUtils.training(trainingParmsEntity, dataList);
calibrationAlgorithm.setTrainingStatus(TrainingStatusEnum.COMPLETED.getValue());
} catch (Exception e) {
log.error("训练失败,异常信息:{}", e.getMessage());
calibrationAlgorithm.setTrainingStatus(TrainingStatusEnum.TRAINING_FAILED.getValue());
}
log.info("更新状态...");
log.info("entityId:{}", entityId);
log.info("calibrationAlgorithm:{}", calibrationAlgorithm);
calibrationAlgorithm.setId(entityId);
this.update(calibrationAlgorithm);
return null;
};
return executorService.submit(task);
}
public void timeoutCheck(Future<Void> future, String entityId) {
checkService.execute(() -> {
try {
log.info("提交任务,entityId:{}", entityId);
future.get(24, TimeUnit.HOURS);
} catch (TimeoutException e) {
log.info("计算超时");
TCalibrationAlgorithm calibrationAlgorithm = new TCalibrationAlgorithm();
calibrationAlgorithm.setId(entityId);
calibrationAlgorithm.setTrainingStatus(TrainingStatusEnum.TRAINING_FAILED.getValue());
this.update(calibrationAlgorithm);
future.cancel(true);
} catch (InterruptedException | ExecutionException e) {
log.error("训练失败,发生异常", e);
TCalibrationAlgorithm calibrationAlgorithm = new TCalibrationAlgorithm();
calibrationAlgorithm.setId(entityId);
calibrationAlgorithm.setTrainingStatus(TrainingStatusEnum.TRAINING_FAILED.getValue());
this.update(calibrationAlgorithm);
future.cancel(true);
}
});
}