如何在Airflow中动态创建子标记

时间:2018-02-23 12:24:26

标签: python-3.x etl airflow

我有一个主dag,它检索文件并将此文件中的数据拆分为单独的csv文件。 我必须为这些csv文件的每个文件执行另一组任务。例如(上传到GCS,插入到BigQuery) 如何根据文件数动态生成每个文件的SubDag? SubDag将定义上传到GCS,插入BigQuery,删除csv文件等任务。

现在,这就是它的样子

main_dag = DAG(....)
download_operator = SFTPOperator(dag = main_dag, ...)  # downloads file
transform_operator = PythonOperator(dag = main_dag, ...) # Splits data and writes csv files

def subdag_factory(): # Will return a subdag with tasks for uploading to GCS, inserting to BigQuery.
    ...
    ...

如何为transform_operator中生成的每个文件调用subdag_factory?

2 个答案:

答案 0 :(得分:2)

我尝试如下动态创建subdag

# create and return and DAG
def create_subdag(dag_parent, dag_id_child_prefix, db_name):
    # dag params
    dag_id_child = '%s.%s' % (dag_parent.dag_id, dag_id_child_prefix + db_name)
    default_args_copy = default_args.copy()

    # dag
    dag = DAG(dag_id=dag_id_child,
              default_args=default_args_copy,
              schedule_interval='@once')

    # operators
    tid_check = 'check2_db_' + db_name
    py_op_check = PythonOperator(task_id=tid_check, dag=dag,
                                 python_callable=check_sync_enabled,
                                 op_args=[db_name])

    tid_spark = 'spark2_submit_' + db_name
    py_op_spark = PythonOperator(task_id=tid_spark, dag=dag,
                                 python_callable=spark_submit,
                                 op_args=[db_name])

    py_op_check >> py_op_spark
    return dag

# wrap DAG into SubDagOperator
def create_subdag_operator(dag_parent, db_name):
    tid_subdag = 'subdag_' + db_name
    subdag = create_subdag(dag_parent, tid_prefix_subdag, db_name)
    sd_op = SubDagOperator(task_id=tid_subdag, dag=dag_parent, subdag=subdag)
    return sd_op

# create SubDagOperator for each db in db_names
def create_all_subdag_operators(dag_parent, db_names):
    subdags = [create_subdag_operator(dag_parent, db_name) for db_name in db_names]
    # chain subdag-operators together
    airflow.utils.helpers.chain(*subdags)
    return subdags


# (top-level) DAG & operators
dag = DAG(dag_id=dag_id_parent,
          default_args=default_args,
          schedule_interval=None)

subdag_ops = create_subdag_operators(dag, db_names)

请注意,可以在subdag文件中静态声明要为其创建db_names的输入的列表(此处为python),也可以从外部源中读取该列表。

生成的DAG看起来像这样 enter image description here enter image description here

潜入SubDAG(s)

enter image description here

enter image description here

答案 1 :(得分:1)

Airflow以两种不同的方式处理DAG。

  1. 在python文件中定义DAG并将其放入dags_folder时的一种方法。 DAG结构变化较小。所以起初我做错了。对于目录中的每个文件,我为每个气流调度程序的心跳生成了一个DAG,我的代码遍历了所有文件并生成了DAG。优点:)不是很多。很多,如果与DAG相对应的文件被删除,你无法控制DAG,你看不到步骤,你就无法重启等等。

  2. 所以在某些时候我想出了另一个解决方案。你有静态DAG(它们仍然是动态的,脚本会生成它们,但它们的结构,ID不会改变)。因此,而不是一个脚本在目录中行走并生成DAG。你做了两个静态DAG,一个定期监视目录(* / 10 ****),另一个由第一个触发。因此,当出现新文件/文件时,第一个DAG使用arg conf触发第二个文件/文件。必须为目录中的每个文件执行下一个代码。

        session = settings.Session()
        dr = DagRun(
                    dag_id=dag_to_be_triggered,
                    run_id=uuid_run_id,
                    conf={'file_path': path_to_the_file},
                    execution_date=datetime.now(),
                    start_date=datetime.now(),
                    external_trigger=True)
        logging.info("Creating DagRun {}".format(dr))
        session.add(dr)
        session.commit()
        session.close()
    
  3. 触发的DAG可以接收conf arg并完成特定文件的所有必需任务。要访问conf param,请使用:

        def work_with_the_file(**context):
            path_to_file = context['dag_run'].conf['file_path'] \
                if 'file_path' in context['dag_run'].conf else None
    
            if not path_to_file:
                raise Exception('path_to_file must be provided')
    
    

    优点Airflow的所有灵活性和功能

    监视器DAG可能是垃圾邮件。