默认情况下,任务只会在所有父任务成功完成后执行。这通常是预期的行为。但是如果你想要更复杂的东西呢?如果希望在一个上游任务成功后立即执行该任务,该怎么办?或者在另一组任务失败时执行另一组任务?在处理数据流程的多个用例时,掌握这个概念是必不可少的:Ariflow触发规则。
应用场景
让我们从下面具体的用例开始,以便更好地理解为什么这个特性可能对你有帮助。
任务失败响应
当任务失败时你能做什么?Airflow提供了不同的机制,但在失败的情况下,常见的反应是使用回调。这很简单:给Operator参数on_failure_callback提供函数,一旦任务失败,它就会调用该函数。它很棒,但也有一些局限性:
- 调度器不管理回调。如果失败,则无法重试或得到警告。
- 回调函数应该运行轻量级任务。如果需要实现更复杂业务,应该采用其他方案。
解决这些缺点的方法是使用Airflow触发规则!
解决BranchPythonOperator缺陷
让我们看一下下面的数据流程:
choose_model使用BranchPythonOperator在is_inaccurate
和is_accurate
之间进行选择,然后不管选择的任务是什么都执行store。然而,你可以从上面看到,事情并不如预想那样执行。当一个任务被跳过时,其所有直接下游任务都被跳过。如果你想始终都要执行store呢?Airflow触发规则该如何设置?
Airflow 触发规则
触发器规则定义了任务运行的前提——基于什么条件。默认情况下,所有任务都具有相同的触发规则all_success,即如果一个任务的所有上游任务都成功,则该任务运行。只能指定一个触发规则:
my_task = PythonOperator(
task_id='my_task',
trigger_rule='all_success'
)
触发规则有很多。让我们看看每一个。
Airflow触发规则详解
- all_success
这个很简单,你已经用过了。当所有直接上游任务都成功时,任务运行。
然而,如果上游任务被跳过,那么下游任务也会被跳过:
- all_failed
很简单,如果所有直接上游任务都失败,则运行一个任务
如果你想做一些清理或更复杂的事情,你不能在回调中实现,这个Airflow触发规则是很方便的。你可以定义一组任务,以便在某些任务失败时执行。与all_success一样,如果跳过一个直接上游任务,也会跳过该任务。
- all_done
在所有直接上游任务完成后立即触发任务,而不管它们的状态如何。
如果你总是希望执行某个任务,而不管上游任务的状态如何,例如清理某些资源,则此触发器规则可能很有用。
- one_failed
只要上游任务有一个失败,你的任务会执行。
如果有长时间运行的任务,并且希望在其中一个任务失败后立即执行某些操作,则非常有用。
- one_success
就像one_failed一样,但是相反。只要其中一个上游任务成功,你的任务就会运行。
- none_failed
如果所有直接上游任务都成功或被跳过,则任务运行。只有你想要处理跳过状态时才有用。
- none_skipped
如果没有直接上行任务处于跳过状态,则任务运行。
- none_failed_min_one_success
先前称为“none_failed_or_skip”(在气流2.2之前),使用此触发规则,如果所有直接上游任务都没有失败并且至少有一个成功,则你的任务运行。
这是处理BranchPythonOperator缺陷时必须使用的规则。
规则配置实战
基本示例(使用 Python API)
假设你有一个简单的 DAG(有向无环图),其中包含三个任务task1
、task2
和task3
。你想设置task3
的触发规则为one_failed
。以下是示例代码:
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from datetime import datetime
default_args = {
'owner': 'airflow',
'start_date': datetime(2024, 1, 1)
}
dag = DAG('trigger_rule_example', default_args=default_args, schedule_interval=None)
task1 = DummyOperator(task_id='task1', dag=dag)
task2 = DummyOperator(task_id='task2', dag=dag)
task3 = DummyOperator(task_id='task3', dag=dag, trigger_rule='one_failed')
task1 >> task3
task2 >> task3
在这个示例中:
- 首先,导入了必要的模块,包括
DAG
和DummyOperator
。DummyOperator
是一个简单的占位符任务,用于演示目的。 - 定义了
default_args
,其中包含owner
(任务所有者)和start_date
(DAG 开始日期)等默认参数。 - 创建了一个名为
trigger_rule_example
的 DAG,设置了schedule_interval
为None
,表示这个 DAG 不是按照时间间隔调度的(可以根据实际需求修改)。 - 创建了三个任务
task1
、task2
和task3
。对于task3
,通过trigger_rule='one_failed'
设置了触发规则。 - 最后,通过
>>
操作符定义了任务之间的依赖关系,task1
和task2
的输出都流向task3
。这意味着task3
会根据one_failed
触发规则,在task1
或者task2
失败时被触发。
使用 TaskFlow API 示例(较新的方式)
以下是一个使用 TaskFlow API 设置触发规则的示例。假设你有一个简单的函数式任务的 DAG:
from airflow.decorators import dag, task
from datetime import datetime
@dag(dag_id='taskflow_trigger_rule_example', start_date=datetime(2024, 1, 1), schedule_interval=None)
def my_dag():
@task
def task1():
return 1
@task
def task2():
# 模拟一个可能的错误
raise ValueError("Error in task2")
@task(trigger_rule='one_failed')
def task3(result1, result2):
try:
# 尝试处理结果,这里只是简单打印
print(f"Task3 received results: {result1}, {result2}")
except Exception as e:
print(f"Error in task3: {e}")
result1 = task1()
result2 = task2()
task3(result1, result2)
my_dag = my_dag()
在这个示例中:
- 使用
@dag
装饰器定义了一个 DAG,包含start_date
和schedule_interval
等参数。 - 定义了三个任务
task1
、task2
和task3
。task1
返回一个简单的值,task2
模拟了一个错误(抛出ValueError
)。 - 对于
task3
,通过@task(trigger_rule='one_failed')
设置了触发规则。task3
尝试接收task1
和task2
的结果并进行处理。由于task2
会出错,根据one_failed
规则,task3
会被触发并处理可能的错误情况。
上面示例展示了如何在 Airflow 中配置触发规则,无论是使用传统的 Python API 还是较新的 TaskFlow API。你可以根据实际的业务需求,调整触发规则和任务的具体逻辑。
Taskflow API为定义Python任务及其依赖项提供了一种简单的方法,使用的语法更接近于使用常规Python函数,而不是更面向对象的操作符API。这使得API可以极大地简化大量使用PythonOperator的dag,并使用xcom在结果任务之间传递数据。通过确保在任务之间显式地传递值,而不是在相应的函数中隐藏任务之间的依赖关系,该API还解决了使用xcom传递数据的不足。
然而,Taskflow API的一个缺点是它的使用目前仅限于Python任务,否则将使用PythonOperator实现。因此,涉及任何其他Airflow操作的任务都需要使用常规API来定义任务及其依赖关系。尽管这并不妨碍混合和匹配这两种风格,但如果不小心,生成的代码可能会变得令人困惑。
总结
Airflow触发规则使用简单,但功能强大,因为它们可以解决实际场景问题。不要犹豫,使用触发规则比回调更可靠、高效处理错误。触发规则对于管理资源非常有用,但是需要你采用更好的方法来创建或和拆除任务。