Python 在气流中创建动态工作流的正确方法 问题
气流中是否有任何方法可以创建一个工作流,使得任务B*的数量在任务a完成之前是未知的?我已经看过了子Dag,但看起来它只能处理一组静态任务,这些任务必须在Dag创建时确定 dag触发器会工作吗?如果是的话,请你举个例子 我有一个问题,在任务A完成之前,不可能知道计算任务C所需的任务B的数量。每个任务B.*将花费数小时进行计算,并且无法合并Python 在气流中创建动态工作流的正确方法 问题,python,workflow,airflow,Python,Workflow,Airflow,气流中是否有任何方法可以创建一个工作流,使得任务B*的数量在任务a完成之前是未知的?我已经看过了子Dag,但看起来它只能处理一组静态任务,这些任务必须在Dag创建时确定 dag触发器会工作吗?如果是的话,请你举个例子 我有一个问题,在任务A完成之前,不可能知道计算任务C所需的任务B的数量。每个任务B.*将花费数小时进行计算,并且无法合并 |---> Task B.1 --| |---> Task B.2 --| Task A
|---> 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,其中包含以下内容:
def return\u list()
)父任务.get任务\u实例(settings.Session,start\u date=parent\u dag.get\u active\u runs()[-1])[-1]
)筛选出当前运行的任务,您可能可以在此处添加更多筛选器dag\U id='%s.%s'(父dag\U名称,'test1')
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