Airflow 如何编写一个具有相对于执行日期的动态任务集的DAG?
我们有一个DAG,它从一个广告平台上获取一些数据。这些广告被组织成活动。我们的目标是为这些活动引入高级指标。为此,我们首先需要获得给定执行日期的活动列表——幸运的是,如果我们知道要查询的时间范围,广告平台的API使这一点变得微不足道 目前,我们的DAG的结构是去获取这些活动,然后将它们存储在S3中,最后是红移。然后,我们在设置后续任务之前查询Redshift,这些任务提取每个活动的数据。这是总的部分。我们也可以查看S3,但问题是键是用Airflow 如何编写一个具有相对于执行日期的动态任务集的DAG?,airflow,Airflow,我们有一个DAG,它从一个广告平台上获取一些数据。这些广告被组织成活动。我们的目标是为这些活动引入高级指标。为此,我们首先需要获得给定执行日期的活动列表——幸运的是,如果我们知道要查询的时间范围,广告平台的API使这一点变得微不足道 目前,我们的DAG的结构是去获取这些活动,然后将它们存储在S3中,最后是红移。然后,我们在设置后续任务之前查询Redshift,这些任务提取每个活动的数据。这是总的部分。我们也可以查看S3,但问题是键是用ds宏的值模板化的。在构建DAG本身时,似乎没有办法知道该值
ds
宏的值模板化的。在构建DAG本身时,似乎没有办法知道该值
我们当前的方法也不知道执行日期,所以它总是查询所有活动,即使这些活动在我们感兴趣的时间段内没有活动
为了让这更具体一点,这里是DAG今天的样子:
另一种方法是将所有这些汇总到一个操作符中,该操作符封装获取当前执行日期的活动集,然后获取每个活动的度量。我们避免了这一点,因为这似乎排除了通过每个活动的单独任务并行提取数据
我们如何编写此DAG,以便通过动态查询活动的红移表来维护提供的并行化,但活动被正确地限制在执行日期?我认为这是不可能的。DAG只能在由DAG的python定义定义的一种配置中渲染。例如,您将无法控制DAG的哪个版本作为执行日期的函数进行渲染,因此无法回顾DAG在过去的渲染方式。如果希望根据执行日期呈现当前DAG,则可以在DAG的python定义中编写一些逻辑
根据您编排气流作业的方式,您可能可以使用所述的单个操作员,但该操作员可以在红移时启动并行查询,并在所有查询完成后终止。,为了节省时间,我将把来自第三方的想法和代码示例拼凑在一起。我将对这些来源给予赞扬,以便您可以查看上下文和文档。另外一个警告是,我还没有能够测试这个,但我99%肯定这会起作用 整个操作中棘手的部分将是找出如何处理可能已经结束并重新开始的活动。气流不会像具有移动开始或停止日期的DAG。移动停止日期可能会更好一些,移动dag的开始日期根本不起作用。也就是说,如果有活动延长,只要连续性没有差距,您就应该能够移动结束日期。如果你有一个活动失败了,然后在两个活动日之间延长了几天,你可能会想弄清楚如何让这两个活动看起来像独特的活动 第一步 您需要创建一个python脚本,该脚本将调用您的数据库并从您的活动中返回相关的详细信息。假设它在MySQL中,它看起来像这样,一个示例连接来自: 第二步 您将希望通过该光标进行迭代,并动态创建DAG,类似于此示例:
如果您将所有这些代码保存在一个文件中,则需要将其放入您的dags文件夹中。当一个新的活动出现在数据库中时,您将从中创建一个dag,并可以使用您的子dag体系结构使用从该MySQL数据库提取的参数运行完全相同的步骤/任务集。为了安全起见,并在dag列表中保留最近的活动,我将使用日期缓冲区编写mysql查询。这样,您的列表中仍然有最近结束的DAG。在这些dag结束的那天,您应该填充dag的
end\u date
参数。我是否正确理解您在特定时间段内有特定活动,并且如果时间段相关,您希望动态创建dag任务?@trejas这听起来是一个准确的理解,是的。例如,我可能会在6月份开展一项活动,并在7月份结束这项活动。在这一点上,它不再处于活动状态,也不会出现在广告平台的API响应中,以供将来查询。您提前知道每个活动的长度吗?活动是否会被延长或缩短?不幸的是,我们不知道活动实际上是在我们外部处理的,如果不查询广告平台的API,就很难知道。我正在努力理解这个问题,对我来说不是很清楚。您是否动态创建并行任务,以便每个任务获取单个活动数据?它是基于之前获取的活动元数据构建的?我不确定ds模板是如何在这里发挥作用的,你能解释一下吗?创建处理不再活动的活动的任务的实际问题是什么?“如果您希望当前DAG基于执行日期进行渲染,那么您可以在DAG的python定义中编写一些逻辑。”如果可能,为什么任何任意执行日期都不可能?绝对可能。只是需要一些特定的抽象。将在今天晚些时候写一个答案。@maxcountryman您将能够渲染当前且唯一的DAG形状,但无法回顾DAG形状过去是什么,因此无法为任何任意的执行日期进行渲染。但看起来我可能误解了这个问题,所以我期待着特雷哈斯的回答
import pymysql.cursors
# Connect to the database
connection = pymysql.connect(host='localhost',
user='user',
password='passwd',
db='db',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)
try:
with connection.cursor() as cursor:
# Create a new record
sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
cursor.execute(sql, ('webmaster@python.org', 'very-secret'))
# connection is not autocommit by default. So you must commit to save
# your changes.
connection.commit()
with connection.cursor() as cursor:
# Read a single record
sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
cursor.execute(sql, ('webmaster@python.org',))
result = cursor.fetchall()
finally:
connection.close()
from datetime import datetime
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
def create_dag(dag_id,
schedule,
dag_number,
default_args):
def hello_world_py(*args):
print('Hello World')
print('This is DAG: {}'.format(str(dag_number)))
dag = DAG(dag_id,
schedule_interval=schedule,
default_args=default_args)
with dag:
t1 = PythonOperator(
task_id='hello_world',
python_callable=hello_world_py,
dag_number=dag_number)
return dag
# build a dag for each number in range(10)
for campaign in result: # This is the pymysql result from above
dag_id = 'hello_world_{}'.format(str(n))
default_args = {'owner': 'airflow',
'start_date': datetime(2018, 1, 1)
}
schedule = '@daily'
dag_number = n
globals()[dag_id] = create_dag(dag_id,
schedule,
dag_number,
default_args)