Airflow 使用XCOM值在气流中创建动态工作流

Airflow 使用XCOM值在气流中创建动态工作流,airflow,airflow-2.x,Airflow,Airflow 2.x,现在,我使用这样一个变量创建了多个任务,效果很好 with DAG(....) as dag: body = Variable.get("config_table", deserialize_json=True) for i in range(len(body.keys())): simple_task = Operator( task_id = 'task_' + str(i), .....

现在,我使用这样一个变量创建了多个任务,效果很好

with DAG(....) as dag:
    body = Variable.get("config_table", deserialize_json=True)
    for i in range(len(body.keys())):
        simple_task = Operator(
            task_id = 'task_' + str(i),
            .....

但出于某种原因,我需要使用XCOM值,而不是使用变量。 是否可以使用XCOM pull值动态创建任务

我试图像这样设置值,但它不起作用


body=“{{ti.xcom\u pull(key='config\u table',task='get\u config\u table')}”
这是不可能的,通常不建议执行动态任务:

  • 气流调度器的工作方式是读取dag文件,将任务加载到内存中,然后检查需要调度的dag和任务,而xcom是与特定dag运行相关的运行时值,因此调度器无法依赖xcom值
  • 当使用动态任务时,您会使自己的调试变得更加困难,因为用于创建dag的值可能会更改,并且您将在不了解原因的情况下失去对日志的访问
  • 您所能做的是使用分支运算符,使这些任务始终存在,并根据xcom值跳过它们。 例如:

    def branch_func(**context)
        return f"task_{context['ti'].xcom_pull(key=key)}"
    
    
    branch = BranchPythonOperator(
        task_id="branch",
        python_callback=branch_func
    )
    
    tasks = [BaseOperator(task_id=f"task_{i}") for i in range(3)]
    branch >> tasks
    

    在某些情况下,使用这种方法也不太好(例如,当我有100个可能的任务时),在这种情况下,我建议编写自己的操作符或使用一个PythonOperator。

    可以从以前任务生成的
    xcom
    动态创建任务,关于这个主题有更广泛的讨论,例如在这方面。建议的方法之一遵循此结构,下面是我制作的一个工作示例:

    示例_file.json:

    {
    “城市”:[“伦敦”、“巴黎”、“英国”、“纽约”]
    }
    
    • 从API、文件或任何来源获取数据。按
      XCom
      的方式推送
    
    def_过程_获得的_数据(ti):
    城市列表=ti.xcom\u pull(任务id='get_data')
    Variable.set(key='list'u of_cities',
    value=list_of_cities['cities'],serialize_json=True)
    def_read_file():
    将open('dags/sample_file.json')作为f:
    data=json.load(f)
    #使用return推送到XCom
    返回数据
    使用DAG('dynamic_tasks_example',schedule_interval='@once',
    开始日期=日前(2),
    catchup=False)作为dag:
    get_data=PythonOperator(
    task_id='get_data',
    python\u callable=\u read\u文件)
    
    • 添加第二个任务,该任务将从pull from
      XCom
      中提取,并使用稍后用于迭代的数据设置一个
      变量
    preparation\u task=PythonOperator(
    任务\u id='准备任务',
    python\u callable=\u进程\u获取的\u数据)
    
    *当然,如果您愿意,可以将两个任务合并为一个任务。我不喜欢这样做,因为通常,我会获取获取数据的一个子集来创建
    变量

    • 读取该
      变量
      ,然后对其进行迭代。定义
      default\u var
      非常关键
    end=dummy运算符(
    任务_id='end',
    触发器(规则=“无”\u失败”)
    #DAG块内的顶级代码
    iterable\u list=变量.get('list\u of\u cities',
    default_var=['default_city'],
    反序列化(json=True)
    
    • 在循环中声明动态任务及其依赖项。使
      任务id
      唯一<代码>任务组
      是可选的,可帮助您对UI进行排序
    
    使用任务组(“动态任务组”,
    前缀组id=False,
    )作为动态任务组:
    对于索引,枚举中的城市(iterable_列表):
    说你好=蟒蛇操作员(
    task_id=f'say_hello_from_{city}',
    python\u callable=\u print\u问候语,
    op_kwargs={'city_name':city'greeting':'Hello'}
    )
    说再见(
    task_id=f'say_debye_from_{city}',
    python\u callable=\u print\u问候语,
    op_kwargs={'city_name':city,'greeting':'bye'}
    )
    #任务组级依赖项
    说你好>>说再见
    #DAG级依赖关系
    获取数据>>准备任务>>动态任务组>>结束
    
    DAG图形视图:

    进口:

    导入json
    从气流导入DAG
    从airflow.utils.dates导入天\u
    从airflow.models导入变量
    从afflow.operators.python_operator导入PythonOperator
    从airflow.operators.dummy导入DummyOperator
    从afflow.utils.task_组导入任务组
    
    要记住的事情:

    • 如果您同时运行相同的
      dag
      ,则所有dag\u都将使用相同的变量,因此您可能需要通过区分它们的名称使其“唯一”
    • 读取
      变量时必须设置默认值,否则,第一次执行可能无法由
      计划程序处理
    • 气流图视图UI可能不会立即刷新更改。尤其是在从创建动态任务生成的iterable中添加或删除项后的第一次运行中发生
    • 如果需要读取多个变量,请务必记住,建议将它们存储在一个JSON值中,以避免不断创建到元数据数据库的连接(本例中的示例)

    祝你好运

    很好,应该注意TaskGroup是2.0+功能,仅供参考。这很有帮助!!