Scheduler是Airflow中最核心部分,下面根据Scheduler启动源码,来讲讲调度的逻辑,如有错误之处,还请留言一起探讨。
调度逻辑(airflow scheduler)
- Scheduler启动执行
airflow scheduler命令,该命令命中airflow/bin/cli.py中的scheduler()方法,该方法根据传参实例化SchedulerJob,然后执行其run()方法;
# scheduler启动命令,生产仅仅指定了dags路径
airflow scheduler -sd /data/airflow_dags
-
SchedulerJob是BaseJob的子类,run()方法是父类m模板方法:- 往job表插入一条job数据(SchedulerJob实例数据),
job_type=SchedulerJob; - 调用子类
_execute()来实现调度; - 如果
_execute()无异常执行或者SystemExit,job都算成功;如果出现异常,则job失败;
- 往job表插入一条job数据(SchedulerJob实例数据),
-
_execute()方法是SchedulerJob实现的:- 初始化
DagFileProcessorAgent对象,处理DAG文件的代理程序,它负责在整个调度过程中所有与DAG解析相关的工作。 DagFileProcessorAgent会创建Scheluer的子进程DagFileProcessorManager,而DagFileProcessorManager会为每一个dag文件创建一个DagFileProcessor进程,来处理dag文件并收集DAG文件的解析结果,并在解析dag文件的过程中,进行进程间通信,向scheluer主进程汇报文件处理结果。 - 调用
_execute_helper()实现调度逻辑;
- 初始化
-
_execute_helper()是调度最核心的逻辑方法,调度的循环逻辑也在其中:self.executor.start(),executor对象已经在SchedulerJob实例化时根据airflow.cfg配置初始化了。所以此处调用其实是KubernetesExecutor.start(),初始化了任务队列、结果队列,包括kube操作客户端等等。
def start(self): self.worker_uuid = KubeWorkerIdentifier.get_or_create_current_kube_worker_uuid() KubeResourceVersion.reset_resource_version() # 初始化两个queue,task_queue放任务,result_queue是收集的dag self.task_queue = self._manager.Queue() self.result_queue = self._manager.Queue() self.kube_client = get_kube_client() # 初始化K8S的调度器,该调度器主要实现了K8S起pod,执行任务 self.kube_scheduler = AirflowKubernetesScheduler( self.kube_config, self.task_queue, self.result_queue, self.kube_client, self.worker_uuid, self.sync_state_buffer ) self._inject_secrets() if self.sync_state_buffer: self.clear_not_launched_queued_tasks()-
self.reset_state_for_orphaned_tasks():在启动scheduler程序的时候,将非backfill前缀的而状态为RUNNING的的DagRun下状态为SCHEDULED,QUEUED的tis的状态重置为None,这样可以让scheduler程序后续将这些tis正常调度; -
self.processor_agent.start():启动多进程收集dag,将收集的dag放入executor的result_queue中; -
接下来是一个无限循环,不断的获取dag,然后调度
self.processor_agent.harvest_simple_dags():将执行器result_queue中的dag拿过来。self._change_state_for_tis_without_dagrun:- 处理那些dag_run中状态为非running的的task_instance,如果task_instance的状态为up_for_retry,但是其dag_run不是running状态,那么将task_instance的状态设置为failed,后续不再调度它们;
- 如果task_instance的状态为scheduled/queued/up for reschedule,但是其dag_run的状态不是running那么将其状态设置为None,后续不再调度它们;
_execute_task_instances:将可执行的任务状态更新为SCHEDULED;self.executor.heartbeat():- 将代执行任务,发送信息给celery队列,信息包含dag_id、task_id、cmd(
airflow run xxx)、dag完整路径等,执行器的execute_async()方法; - 所有任务都发给队列之后,执行执行器的
sync()方法,这里调kube_scheduler.run_next(task)去起pod执行任务;
- 将代执行任务,发送信息给celery队列,信息包含dag_id、task_id、cmd(
self._process_executor_events():处理执行器的一些事件,比如(retry、更新状态等)
-
self.processor_agent.terminate():循环外终止dag收集进程; -
self.executor.end():循环外执行器结束;
到这里其实调度的逻辑就结束了,scheduler会起很多的pod去执行任务,在pod中是执行的airlow run命令:
运行逻辑(airflow run)
- 执行器起pod,pod中run airflow的镜像,传入需要被执行的任务相关参数,执行如下命令:
airflow run CASE_STUDY_GUJING_MONTHLY select_monthly_data_from_mysql_task 2021-11-05T08:00:00+00:00 --local --pool default -sd /Users/user/Docker/airflow/dags/CASE_STUDY_GUJING_MONTHLY.py
LocalTaskJob.run(),调用父类BaseJob的run()方法,会插入一条job记录,job_type=LocalTaskJob,然后调用子类实现的_execute()方法执行,如果正常执行,则成功,如果手动退出也成功,如果出现异常,则为失败。
说明:基于 KubernetesExecutor执行器
招聘
小红书,目前为数不多的靠谱的互联网独角兽,想上车的同学看过来呀~
急聘大数据开发、数据平台开发,其他部门岗位可以帮忙内推,求贤若渴...
感兴趣的留言加微信聊!!!