在作业设计器中,实用哪种作业工作流的引擎是最主要的,现在对Azkaban和Oozie进行调研。
Azkaban是一个类似于Oozie的任务调度系统,它以flow为执行单位进行调度,flow为预定义好的一个或者多个有依赖关系的Job工作流。同时它兼容所有的Hadoop版本,使用Web界面追踪每个任务的执行情况并且提供了邮件的支持。
Azkaban主要有三个组件组成:
名称 |
作用 |
Mysql |
项目以及执行计划(所有任务的属性信息、执行计划、执行的结果以及输出),每次执行情况等信息 |
Web服务器 |
使用jetty做为web容器,提供服务 |
Executor执行服务器 |
负责具体的工作流的提交和执行,可以提交启动多个执行服务器,他们通过mysql进行协调任务执行。 |
下面将通过Azkaban提供的solo(类似于单例模式)模式,对其提交和执行工作流做测试。
入下图所示,为Azkaban的操作主页面。
经过调研,Azkaban可以调度的任务类型有:shell命令,Java程序,MR,Spark,Hive。下面将通过对以上几类任务进行调度过程调研
创建工程:
一个工程包含一个或者多个flows,一个flows包含多个job。job是在Azkaban中运行的一个进程,一个job可能依赖于别的job,多个job和他们之间的依赖组成图表称为flow。
Job创建:
只要创建一个以.job为结尾的文本文件就可以,例如:
这种type为command,告诉Azkaban使用linux的命令或者shell脚本去执行。这是一个job,如果是多个job我们可以定义flow。
Flow创建:
定义多个job以及job之间的依赖就可以组成flow。定义依赖可以使用dependencies参数就可以了。例如创建了4个job:
start.job |
type=command command=sleep 5 command.1=echo "start execute" |
test.job |
type=command command=sleep 3 command.1=echo "Hello World" |
sleep.job |
type=command dependencies=test, start command=sleep 10 |
finish.job |
type=command dependencies=sleep command=sleep 10 |
在以上4个job中,第三个sleep.job是依赖于test和start.job。也就是sleep的job执行顺在需要等待test和start两个job执行完成。同理finish依赖于sleep,finishi.job在sleep执行成功后再进行。
将这4个.job文件打包成zip文件上传至Azkaban服务器(目前Azkaban只支持zip格式的文件上传)。之后会生成job之间的关系以及一张DAG flow图。
当点击执行任务后,会依次执行节点并且记录节点的运行情况。
其中会记录每个job的运行情况以及job的输出。
同理Shell任务的job执行次序,底层修改只是调用不同的shell语句进行执行。例如使用spark做为测试。
首先写好一个job文件:
type=command
command=spark-submit --master spark://10.2.216.33:7077 --class com.test.AzkabanTest testSparkAzkaban.jar
将文件与测试的testSparkAzkaban.jar进行打包zip格式。上传至服务器。运行成功后jobLogs会打印如下结果:
同理MR Job将jar包以及输出的文件也可以放在HDFS中,通过shell语言进行。
Azkaban的使用方式是按照job文件描述来进行使用的,使用type来执行执行任务的类型,通过dependencies可以用来连接任务,即dependencies=A,只有A任务执行完了才可以执行改任务。
其执行任务的类型有:
1.Shell语言类型,在上面已经有相应的解释。
2.Java文件:使用Java和HadoopJava两种类型。这两种是插件形式操作java作业,统一要求的是操作从run方法进行执行,而不是main方法。
3.Hive.
Type=hive
User.to.proxy=azkaban
Azk.hive.action=execute.query
Hive.query.01=sql
由于Azkaban中的每个job都是一个进程,在Azkaban中判断job成功与否是根据这个进程是否成功执行完成,但是在MR 或者Spark Job执行的过程中,如果代码出错,运行在集群上的任务会停止,并不会有内容写入目标文件中,此时返回给Azkaban的进程是执行成功的,也就是job节点执行成功。这与任务执行的结果相悖。
例如:
在执行某个jar包的过程中时,出现了NullPointException,此时MR作业停止,但是最终Process 显示的为执行成功。并且节点最终执行的结果也为成功:
所以为了防止依赖的节点出现错误,其以下节点仍可运行的情况。需要换一个校验job是否正确执行的维度进行评判,比如检测MR 或者 Spark 任务的log文件是否正确执行等,或者检测集群中的任务是否执行成功。
总结:在执行结束后可以返回hdfs中查询是否有对应的文件生成,如果有则表示成功,没有则表示失败。
首先从客户端发送请求入手,客户端会将所有任务封装job防止在zip中,其主要逻辑为:
1.调用LoginAbstractAzkabanServlet。如果是执行任务,会调用其子类ExecutorServlet中的handleAjaxAction。方法。其中会生成一个ExecutableFlow,这个对象中包含了整个工作流的基本信息。
2.生成了exeFlow之后调用executorManager用于提交任务。在这个任务中,会对exeFlow进行资源加锁,填充exeFlow的属性后会上传该工作流的信息到数据库中。最后调用dispatch 调度任务。
3.最终经过重重调用会生成一个ExecutorApiClient作为RestClient向Azkaban发送任务请求。PS.发送的请求是GET请求。传输了一个任务的id。所以以上的所有对exeFlow的操作都是存储与本地的操作。
在服务器端,主要的AzkabanServer包含以下属性:
使用Socket连接的Server以及一个队列线程池。QueuedThreadPool。
1.刚刚在客户端发送的请求是一个Get请求,所以操作是使用doGet方法。该方法中调用handleAjaxExecute。
2.此方法中调用FlowRunnerManager.submitFlow(flowId),在服务器端根据flowId从数据库中获取了指定的flow。
3.根据flow以及相关配置信息会生成一个FlowRunner线程,将此线程放置在线程池中进行执行。其中会使用递归找出所有Ready的节点进行执行。在该线程中会在run()方法中不断执行用户需要的command。
在该方法中会封装command成一个AzkabanProcess继承了process类,调用start()方法去执行相关的命令。
PS 所以在Azkaban中无论执行hadoop还是spark 任务都可以通过设置命令来执行。但是这些命令操作是在Azkaban的服务器上进行的,由于每次操作一条command会生成一个进程去处理,进程量大很可能会影响到服务器的工作。
同时如果直接将command交给执行的用户去执行,可能会出现安全问题。而且对于非技术同学操作困难很大。所以未来如果使用Azkaban进行开发时,需要分析用户的操作生成command,不要将编辑command的入口交给用户。