(8)Airflow之Scheduler逻辑详解

1,809 阅读3分钟

Scheduler是Airflow中最核心部分,下面根据Scheduler启动源码,来讲讲调度的逻辑,如有错误之处,还请留言一起探讨。

调度逻辑(airflow scheduler)

image.png

  1. Scheduler启动执行 airflow scheduler 命令,该命令命中airflow/bin/cli.py中的scheduler()方法,该方法根据传参实例化SchedulerJob,然后执行其run()方法;
# scheduler启动命令,生产仅仅指定了dags路径
airflow scheduler -sd /data/airflow_dags
  1. SchedulerJobBaseJob的子类,run()方法是父类m模板方法:

    • 往job表插入一条job数据(SchedulerJob实例数据),job_type=SchedulerJob
    • 调用子类_execute()来实现调度;
    • 如果_execute()无异常执行或者SystemExit,job都算成功;如果出现异常,则job失败;
  2. _execute()方法是SchedulerJob实现的:

    • 初始化DagFileProcessorAgent对象,处理DAG文件的代理程序,它负责在整个调度过程中所有与DAG解析相关的工作。 DagFileProcessorAgent会创建Scheluer的子进程DagFileProcessorManager,而DagFileProcessorManager会为每一个dag文件创建一个DagFileProcessor进程,来处理dag文件并收集DAG文件的解析结果,并在解析dag文件的过程中,进行进程间通信,向scheluer主进程汇报文件处理结果。
    • 调用_execute_helper()实现调度逻辑;
  3. _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执行任务;
      • self._process_executor_events():处理执行器的一些事件,比如(retry、更新状态等)
    • self.processor_agent.terminate():循环外终止dag收集进程;

    • self.executor.end():循环外执行器结束;

到这里其实调度的逻辑就结束了,scheduler会起很多的pod去执行任务,在pod中是执行的airlow run命令:

运行逻辑(airflow run)

image.png

  1. 执行器起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
  1. LocalTaskJob.run(),调用父类BaseJob的run()方法,会插入一条job记录,job_type=LocalTaskJob,然后调用子类实现的_execute()方法执行,如果正常执行,则成功,如果手动退出也成功,如果出现异常,则为失败。

说明:基于 KubernetesExecutor执行器


招聘

小红书,目前为数不多的靠谱的互联网独角兽,想上车的同学看过来呀~

急聘大数据开发、数据平台开发,其他部门岗位可以帮忙内推,求贤若渴...

感兴趣的留言加微信聊!!!