小bug,伤大脑筋
问题说明
使用springboot集成elastic job,可以进行分片来提高任务运行效率。项目分片分了4片,但是测试起来之后只有两片在运行,由于项目是需要拆分子任务,子任务只见需要串行执行,前后有依赖关系,所以执行完毕需要等待前一个子任务的所有分片运行完毕,在等待的过程中会一直执行,但是另外两个分片一直没有启动,导致任务卡死。
问题原因
开始确实没想过是这个原因,碰了好多壁,也没办法进行远程debug,由于项目是在容器中运行的,在分配的时候只分配了一个虚拟CPU,在配置每个任务的时候是有一个配置是jobExecutorServiceHandlerType来配置每个任务的线程池处理策略,默认就是CPU的处理策略(对应实现类就是CPUUsageJobExecutorServiceHandler),每个任务是在线程池中运行的,线程池的分配又是根据CPU的核心数来决定(处理器数量的2倍),所以实际上容器中运行的时候拿的是虚拟的处理器数量,只有一个,线程池初始和最大的容量就是2,但是分片有4片,而且还在运行,所以剩余两片一直等待,没有运行导致的问题。
关键代码:Runtime.getRuntime().availableProcessors() * 2
public final class CPUUsageJobExecutorServiceHandler extends AbstractJobExecutorServiceHandler {
public CPUUsageJobExecutorServiceHandler() {
}
protected int getPoolSize() {
return Runtime.getRuntime().availableProcessors() * 2;
}
public String getType() {
return "CPU";
}
}
实际上这两种处理方式,CPU和单线程都是通过SPI的方式加载进来的,通过ExecutorContext进行加载,将这两种加载方式加载进来,然后通过get获取。
static {
ElasticJobServiceLoader.registerTypedService(Reloadable.class);
}
问题解决
当然是最简单便捷的,加配置啦,新增了一个虚拟CPU,两个CPU对应4个线程,正好能进行下去,不过实际上多起一个实例也能解决,这样每个作业就是两个实例,实际上就可以正常运行了,不过测试环境嘛,哪可能给你多起一个机器呢,资源宝贵毕竟。
还有就是通过SPI的方式加入另一种线程池处理策略,将这个池子的大小设置为4,就是Runtime.getRuntime().availableProcessors() * 4,也是可以解决的,实现一下AbstractJobExecutorServiceHandler加个文件就可以了
在resources文件夹下建一个META-INF/services文件夹,建一个org.apache.shardingsphere.elasticjob.infra.handler.threadpool.JobExecutorServiceHandler文件,文件里写建立的类的全路径就好了
org.apache.shardingsphere.elasticjob.infra.handler.threadpool.impl.CPUUsageJobExecutorServiceHandler
public final class CPU4UsageJobExecutorServiceHandler extends AbstractJobExecutorServiceHandler {
@Override
protected int getPoolSize() {
return Runtime.getRuntime().availableProcessors() * 2;
}
@Override
public String getType() {
return "CPU4";
}
}
这样在配置文件重需要设置jobExecutorServiceHandlerType设置为你的类里面的Type——“CPU4”