参考链接:
https://blog.bcmeng.com/post/kylin-job.html
https://blog.csdn.net/yu616568/article/details/48104623?_t_t_t=0.29654511851100285
前言
Cube构建过程如下图所示。可以看到,在构建Cube的过程中,会产生很多的任务等待执行。为了协调这些任务的执行过程,能够高效合理的利用资源,就需要用到任务调度机制。
一、Kylin运行模式
Kylin有如下三种运行模式:
1、ALL:默认模式。代表该服务同时用于任务调度和SQL查询。
2、JOB:代表该服务仅用于任务调度,不用于查询。
3、QUERY:代表该服务仅用于查询,不用于构建任务的调度。
不是所有的kylin实例都可以开启任务调度的,只有当实例被设置为ALL和JOB运行模式的时候,需要开启任务调度,这一点从下面一段代码里可以体现出来。
Kylin所使用的的任务调度机制的基于线程池设计的ScheduledExecutor,。其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。ScheduleExecutorService主要的调度方式有两种,基于固定时间间隔的scheduleAtFixedRate,和基于任务执行时间的scheduleWithDelay。Kylin使用的是基于固定时间间隔的任务调度,配置文件中的kylin.job.scheduler.poll-interval-second用于设置这个时间间隔。
单机模式下,对于运行模式为all和job的kylin实例,实例启动时会执行DefaultScheduler的初始化方法。首先从从zookeeper中获取锁,成功获取锁后,初始化两个线程池fecherPool和jobPool,fecherPool负责在线程池中周期性的查看是否有可执行的任务,有就交给jobPool去执行。fecherPool首次调度时延为3秒,之后默认周期为30秒。
二、JOB是如何产生的
当用户在Web UI界面点击构建Cube,就会经过RESTful请求调用JobService的submitJob方法,该方法根据segment信息生成JOB。
Segment信息包括构建类型和时间范围,构建类型包括BUILD、MERGE和REFRESH三种。构建类型不同,就会调用不同构建方法来生成job。以Build类型为例,会调用EngineFactory类中的createBatchCubingJob()方法进行构建。构建算法会根据用户设置选择不同的引擎进行构建,所有的构建算法都继承自IBatchCubingEngine。
构建算法有如下种,现在常用的构建算法应该是MRBatchCubingEngine2和SparkBatchCubingEngine2,从名字上可以看出前者是用MapReduce计算,后者是用Spark构建。
CreateBatchCubingJob调用了BatchCubingJobBuilder2.build()来执行构建过程。将一个Cube构建过程中的所有子JOB按顺序添加以供调度。
代码中可以看到构建一个cube所要执行的步骤。包括打平表,建立字典、构建cube(包括逐层构建和内存中构建两种方法,算法会根据Cube统计信息自动选择构建方法,没有被选择的方法会跳过),最后更新元数据。这些子任务被添加到一个JOB中返回。
三、JOB执行逻辑
Kylin中有一个抽象类AbstractExecutable实现了Executable接口,并实现了其中的Execute核心方法。该类作为所有Job的模板,在Execute()方法中定义了Job的执行逻辑。其中,doWork()方法定义Job具体执行逻辑,留给子类根据自身具体逻辑重写,onExecuteStart,onExecuteError,onExecuteFinished分别在doWork()方法开始前、出错时、完成后执行。
AbstractExecutable的直接继承类如下。
其中后缀为step的类基本都是用于执行Cube构建过程中的步骤。其他主要的子类包括如下几种:
1、ShellExecutable:用于执行shell命令。
2、HadoopShellExecutable:执行依赖Hadoop且用shell提交的任务。
3、MapReduceExecutable:执行MapReduce任务。
4、DefaultChainedExecutable:链接cube构建过程产生Job的容器。
5、BrokenExecutable:发现元数据被损坏时执行。
四、链式执行子JOB
在第三节中我们提到,构建cube过程中的子任务被添加到一个JOB中返回。这个JOB就是继承了DefaultChainedExecutable类,当构建cube的JOB开始执行后,会在doWork()方法中链式的执行它的子任务。从列表中依次取出子任务检查其运行状态,并根据不同的运行状态返回不同的信息。
子任务状态为Running时,等待完成;状态为STOPPED,任务被暂停;状态为ERROR,执行出错,抛出异常;状态为Ready,开始执行。
从这一段代码可以看出,其实每执行一次DefaultChainedExecutable的doWork()方法只会执行一个子任务。那么这是如何做到链式执行所有子任务的呢?可以看一下DefaultChainedExecutable的onExecuteFinished()方法,它会在doWork()方法完成后执行。
可以看到,如果上一个子任务正常执行完毕但是并没有执行完所有的子任务,onExecuteFinished()就会将自身标记为Ready再次返回,这样就会被调度执行下一个子任务,直到所有子任务都完成。
五、总结