背景
项目中开发一个任务执行模块以下简称执行模块,由别的模块调用执行模块计算任务的结果,计算完成后需要把结算结果、计算状态、计算耗时等信息更新到Mysql库里,执行模块是来一个任务执行一次更新一次,后来发现这样需要频繁的与Mysql交互,且需要等待写库结果返回,效率堪忧拖慢其他模块。
优化
第一步:用线程池来更新,将更新代码提交到线程池中,由线程池调度入库
缺点:没有解决与数据库频繁交互的问题。
第二步:执行模块不管更新结果,只需将更新任务放入一个队列中然后直接返回;用Spring的定时任务注解@Scheduled,指定一个方法,隔一段时间调用一次入库方法;入库的逻辑是,获取队列当前任务个数cnt,循环poll任务然后添加到一个List中,poll够cnt个之后,通过批量更新方法将List更新到数据库。
缺点:定时执行无法控制队列大小,可能一次会取出很多条任务,也可能会把队列撑得过大。
第三步:使用阻塞队列放更新任务,用守护线程poll的队列中的任务,当条数等于5000条(此值根据实际情况),则批量更新一次,在poll时设置超时时间为30秒,当超过30秒还是没有取到任务,则也批量把已经渠道的任务更新一次。
总结:没有三板斧都不好意思写代码!!!
队列实现
@Service
public class JobExecutorDataServiceImpl {
//定义一个容量为10000的阻塞队列,BlockingQueue线程安全可以多个生产者同时put
private BlockingQueue<Job> dataQueue = new LinkedBlockingQueue<>(10000);
//put任务的方法,供生产者调用
public void recordJob(Job job) {
try {
dataQueue.put(job);
} catch (InterruptedException e) {
LOGGER.info("批量更新Job入队列异常");
Thread.currentThread().interrupt();
}
}
//初始化即调用
@PostConstruct
private void batchUpdate() {
Thread thread =