Python 在气流中创建动态工作流的正确方法 问题

Python 在气流中创建动态工作流的正确方法 问题,python,workflow,airflow,Python,Workflow,Airflow,气流中是否有任何方法可以创建一个工作流,使得任务B*的数量在任务a完成之前是未知的?我已经看过了子Dag,但看起来它只能处理一组静态任务,这些任务必须在Dag创建时确定 dag触发器会工作吗?如果是的话,请你举个例子 我有一个问题,在任务A完成之前,不可能知道计算任务C所需的任务B的数量。每个任务B.*将花费数小时进行计算,并且无法合并 |---> Task B.1 --| |---> Task B.2 --| Task A

气流中是否有任何方法可以创建一个工作流,使得任务B*的数量在任务a完成之前是未知的?我已经看过了子Dag,但看起来它只能处理一组静态任务,这些任务必须在Dag创建时确定

dag触发器会工作吗?如果是的话,请你举个例子

我有一个问题,在任务A完成之前,不可能知道计算任务C所需的任务B的数量。每个任务B.*将花费数小时进行计算,并且无法合并

              |---> Task B.1 --|
              |---> Task B.2 --|
 Task A ------|---> Task B.3 --|-----> Task C
              |       ....     |
              |---> Task B.N --|
想法#1 我不喜欢这个解决方案,因为我必须创建一个阻塞的ExternalTaskSensor,所有的任务B*需要2-24小时才能完成。所以我不认为这是可行的解决方案。当然有更简单的方法吗?或者气流不是为此而设计的

Dag 1
Task A -> TriggerDagRunOperator(Dag 2) -> ExternalTaskSensor(Dag 2, Task Dummy B) -> Task C

Dag 2 (Dynamically created DAG though python_callable in TriggerDagrunOperator)
               |-- Task B.1 --|
               |-- Task B.2 --|
Task Dummy A --|-- Task B.3 --|-----> Task Dummy B
               |     ....     |
               |-- Task B.N --|
编辑1:
到目前为止,这个问题仍然没有很好的答案。有几个人联系过我,寻求解决方案

下面是我如何在没有任何子数据的情况下通过类似请求完成的:

首先创建一个方法,该方法返回您想要的任何值

def values_function():
     return values
下一个创建方法将动态生成作业:

def group(number, **kwargs):
        #load the values if needed in the command you plan to execute
        dyn_value = "{{ task_instance.xcom_pull(task_ids='push_func') }}"
        return BashOperator(
                task_id='JOB_NAME_{}'.format(number),
                bash_command='script.sh {} {}'.format(dyn_value, number),
                dag=dag)
然后将它们结合起来:

push_func = PythonOperator(
        task_id='push_func',
        provide_context=True,
        python_callable=values_function,
        dag=dag)

complete = DummyOperator(
        task_id='All_jobs_completed',
        dag=dag)

for i in values_function():
        push_func >> group(i) >> complete
OA:“气流中是否有任何方法可以创建一个工作流,使得任务B*的数量在任务a完成之前是未知的?”

简而言之,答案是否定的。气流将在开始运行DAG之前建立DAG流

也就是说,我们得出了一个简单的结论,那就是我们没有这样的需求。 当您想要并行化一些工作时,您应该评估可用的资源,而不是要处理的项目数

我们是这样做的:我们动态地生成一个固定数量的任务,比如说10个,这将分割作业。例如,如果我们需要处理100个文件,每个任务将处理其中10个文件。我将在今天晚些时候发布代码

更新

这是密码,抱歉耽搁了

from datetime import datetime, timedelta

import airflow
from airflow.operators.dummy_operator import DummyOperator

args = {
    'owner': 'airflow',
    'depends_on_past': False,
    'start_date': datetime(2018, 1, 8),
    'email': ['myemail@gmail.com'],
    'email_on_failure': True,
    'email_on_retry': True,
    'retries': 1,
    'retry_delay': timedelta(seconds=5)
}

dag = airflow.DAG(
    'parallel_tasks_v1',
    schedule_interval="@daily",
    catchup=False,
    default_args=args)

# You can read this from variables
parallel_tasks_total_number = 10

start_task = DummyOperator(
    task_id='start_task',
    dag=dag
)


# Creates the tasks dynamically.
# Each one will elaborate one chunk of data.
def create_dynamic_task(current_task_number):
    return DummyOperator(
        provide_context=True,
        task_id='parallel_task_' + str(current_task_number),
        python_callable=parallelTask,
        # your task will take as input the total number and the current number to elaborate a chunk of total elements
        op_args=[current_task_number, int(parallel_tasks_total_number)],
        dag=dag)


end = DummyOperator(
    task_id='end',
    dag=dag)

for page in range(int(parallel_tasks_total_number)):
    created_task = create_dynamic_task(page)
    start_task >> created_task
    created_task >> end
代码说明:

这里我们有一个开始任务和一个结束任务(都是虚拟的)

然后从带有for循环的start任务开始,我们创建了10个具有相同python可调用性的任务。任务在函数create_dynamic_task中创建

我们将并行任务的总数和当前任务索引作为参数传递给每个python可调用对象

假设您有1000个项目需要详细说明:第一个任务将收到输入,它应该详细说明10个块中的第一个块。它将1000个项目分为10个部分,并详细阐述第一个部分。

我发现这与这个问题非常相似。然而,它充满了拼写错误,当我尝试实现它时,它不起作用

我对上述问题的答复如下:


如果要动态创建任务,则必须迭代不是由上游任务创建的或可以独立于该任务定义的任务。
我了解到,不能将执行日期或其他变量传递给模板之外的对象(例如任务)正如许多其他人以前指出的那样。另见

我已经找到了一种根据以前任务的结果创建工作流的方法。
基本上,您要做的是有两个子DAG,其中包含以下内容:

  • Xcom在首先执行的子DAG中推送列表(或以后创建动态工作流所需的任何内容)(请参见test1.py
    def return\u list()
  • 将主dag对象作为参数传递给第二个子dag
  • 现在,如果您拥有主dag对象,则可以使用它获取其任务实例的列表。从任务实例列表中,您可以使用
    父任务.get任务\u实例(settings.Session,start\u date=parent\u dag.get\u active\u runs()[-1])[-1]
    )筛选出当前运行的任务,您可能可以在此处添加更多筛选器
  • 对于该任务实例,可以使用xcom pull通过将dag\U id指定给第一个子dag之一来获取所需的值:
    dag\U id='%s.%s'(父dag\U名称,'test1')
  • 使用列表/值动态创建任务
  • 现在我已经在我的本地气流装置上测试过了,效果很好。如果同时运行多个dag实例,我不知道xcom pull部分是否会有任何问题,但是您可能会使用唯一键或类似的东西来唯一标识所需的xcom值。 有人可能会优化3。步骤100%确保获得当前主dag的特定任务,但就我的使用而言,这执行得足够好,我认为只需要一个task_实例对象就可以使用xcom_pull

    此外,每次执行之前,我都会清理第一个子DAG的XCOM,以确保不会意外获得任何错误的值

    我很不擅长解释,所以我希望下面的代码能把一切都解释清楚:

    test1.py

    from airflow.models import DAG
    import logging
    from airflow.operators.python_operator import PythonOperator
    from airflow.operators.postgres_operator import PostgresOperator
    
    log = logging.getLogger(__name__)
    
    
    def test1(parent_dag_name, start_date, schedule_interval):
        dag = DAG(
            '%s.test1' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date,
        )
    
        def return_list():
            return ['test1', 'test2']
    
        list_extract_folder = PythonOperator(
            task_id='list',
            dag=dag,
            python_callable=return_list
        )
    
        clean_xcoms = PostgresOperator(
            task_id='clean_xcoms',
            postgres_conn_id='airflow_db',
            sql="delete from xcom where dag_id='{{ dag.dag_id }}'",
            dag=dag)
    
        clean_xcoms >> list_extract_folder
    
        return dag
    
    from airflow.models import DAG, settings
    import logging
    from airflow.operators.dummy_operator import DummyOperator
    
    log = logging.getLogger(__name__)
    
    
    def test2(parent_dag_name, start_date, schedule_interval, parent_dag=None):
        dag = DAG(
            '%s.test2' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date
        )
    
        if len(parent_dag.get_active_runs()) > 0:
            test_list = parent_dag.get_task_instances(settings.Session, start_date=parent_dag.get_active_runs()[-1])[-1].xcom_pull(
                dag_id='%s.%s' % (parent_dag_name, 'test1'),
                task_ids='list')
            if test_list:
                for i in test_list:
                    test = DummyOperator(
                        task_id=i,
                        dag=dag
                    )
    
        return dag
    
    from datetime import datetime
    from airflow import DAG
    from airflow.operators.subdag_operator import SubDagOperator
    from subdags.test1 import test1
    from subdags.test2 import test2
    
    DAG_NAME = 'test-dag'
    
    dag = DAG(DAG_NAME,
              description='Test workflow',
              catchup=False,
              schedule_interval='0 0 * * *',
              start_date=datetime(2018, 8, 24))
    
    test1 = SubDagOperator(
        subdag=test1(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval),
        task_id='test1',
        dag=dag
    )
    
    test2 = SubDagOperator(
        subdag=test2(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval,
                     parent_dag=dag),
        task_id='test2',
        dag=dag
    )
    
    test1 >> test2
    
    test2.py

    from airflow.models import DAG
    import logging
    from airflow.operators.python_operator import PythonOperator
    from airflow.operators.postgres_operator import PostgresOperator
    
    log = logging.getLogger(__name__)
    
    
    def test1(parent_dag_name, start_date, schedule_interval):
        dag = DAG(
            '%s.test1' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date,
        )
    
        def return_list():
            return ['test1', 'test2']
    
        list_extract_folder = PythonOperator(
            task_id='list',
            dag=dag,
            python_callable=return_list
        )
    
        clean_xcoms = PostgresOperator(
            task_id='clean_xcoms',
            postgres_conn_id='airflow_db',
            sql="delete from xcom where dag_id='{{ dag.dag_id }}'",
            dag=dag)
    
        clean_xcoms >> list_extract_folder
    
        return dag
    
    from airflow.models import DAG, settings
    import logging
    from airflow.operators.dummy_operator import DummyOperator
    
    log = logging.getLogger(__name__)
    
    
    def test2(parent_dag_name, start_date, schedule_interval, parent_dag=None):
        dag = DAG(
            '%s.test2' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date
        )
    
        if len(parent_dag.get_active_runs()) > 0:
            test_list = parent_dag.get_task_instances(settings.Session, start_date=parent_dag.get_active_runs()[-1])[-1].xcom_pull(
                dag_id='%s.%s' % (parent_dag_name, 'test1'),
                task_ids='list')
            if test_list:
                for i in test_list:
                    test = DummyOperator(
                        task_id=i,
                        dag=dag
                    )
    
        return dag
    
    from datetime import datetime
    from airflow import DAG
    from airflow.operators.subdag_operator import SubDagOperator
    from subdags.test1 import test1
    from subdags.test2 import test2
    
    DAG_NAME = 'test-dag'
    
    dag = DAG(DAG_NAME,
              description='Test workflow',
              catchup=False,
              schedule_interval='0 0 * * *',
              start_date=datetime(2018, 8, 24))
    
    test1 = SubDagOperator(
        subdag=test1(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval),
        task_id='test1',
        dag=dag
    )
    
    test2 = SubDagOperator(
        subdag=test2(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval,
                     parent_dag=dag),
        task_id='test2',
        dag=dag
    )
    
    test1 >> test2
    
    以及主要的工作流程:

    test.py

    from airflow.models import DAG
    import logging
    from airflow.operators.python_operator import PythonOperator
    from airflow.operators.postgres_operator import PostgresOperator
    
    log = logging.getLogger(__name__)
    
    
    def test1(parent_dag_name, start_date, schedule_interval):
        dag = DAG(
            '%s.test1' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date,
        )
    
        def return_list():
            return ['test1', 'test2']
    
        list_extract_folder = PythonOperator(
            task_id='list',
            dag=dag,
            python_callable=return_list
        )
    
        clean_xcoms = PostgresOperator(
            task_id='clean_xcoms',
            postgres_conn_id='airflow_db',
            sql="delete from xcom where dag_id='{{ dag.dag_id }}'",
            dag=dag)
    
        clean_xcoms >> list_extract_folder
    
        return dag
    
    from airflow.models import DAG, settings
    import logging
    from airflow.operators.dummy_operator import DummyOperator
    
    log = logging.getLogger(__name__)
    
    
    def test2(parent_dag_name, start_date, schedule_interval, parent_dag=None):
        dag = DAG(
            '%s.test2' % parent_dag_name,
            schedule_interval=schedule_interval,
            start_date=start_date
        )
    
        if len(parent_dag.get_active_runs()) > 0:
            test_list = parent_dag.get_task_instances(settings.Session, start_date=parent_dag.get_active_runs()[-1])[-1].xcom_pull(
                dag_id='%s.%s' % (parent_dag_name, 'test1'),
                task_ids='list')
            if test_list:
                for i in test_list:
                    test = DummyOperator(
                        task_id=i,
                        dag=dag
                    )
    
        return dag
    
    from datetime import datetime
    from airflow import DAG
    from airflow.operators.subdag_operator import SubDagOperator
    from subdags.test1 import test1
    from subdags.test2 import test2
    
    DAG_NAME = 'test-dag'
    
    dag = DAG(DAG_NAME,
              description='Test workflow',
              catchup=False,
              schedule_interval='0 0 * * *',
              start_date=datetime(2018, 8, 24))
    
    test1 = SubDagOperator(
        subdag=test1(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval),
        task_id='test1',
        dag=dag
    )
    
    test2 = SubDagOperator(
        subdag=test2(DAG_NAME,
                     dag.start_date,
                     dag.schedule_interval,
                     parent_dag=dag),
        task_id='test2',
        dag=dag
    )
    
    test1 >> test2
    

    我想我已经找到了一个更好的解决方案,它通过触发多个DagRuns来使用DagRuns的简单排队,类似于。虽然我不得不修补,使其与最新的气流一起工作,但大部分的功劳都归我所有

    该解决方案使用一个:

    来自气流导入设置的
    
    从airflow.models导入DagBag
    从airflow.operators.dagrun_操作符导入DagRunOrder、TriggerDagRunOperator
    从afflow.utils.decorators导入应用默认值
    从airflow.utils.state导入状态
    从airflow.utils导入时区
    TriggerMultiDagRunOperator类(TriggerDagRunOperator):
    CREATED_DAGRUN_KEY='CREATED_DAGRUN_KEY'
    @应用默认值
    定义初始化(self,op_args=None,op_kwargs=None,
    *args,**kwargs):
    超级(TriggerMultiDagRunOperator,self)。\uuuuuu初始化(*args,**kwargs)
    self.op_args=op_args或[]
    self.op_-kwargs=op_-kwargs或{}
    def