ApacheAirflow学习笔记-2-Task

315 阅读6分钟

DAG代表了完整的工作流,而Task代表了工作流中的具体任务,Task之间的依赖关系代表了任务之间的依赖关系。

1 Task的类型

Task的类型有3种:

  • Operator:Operator是预先定义的Task模板,用Operator创建Task的过程就是编 Operator的构造器传参的过程。Airflow社区提供了大量的Operator,.方便用户实现台 种功能。当然,Airflow也支特自定义Operator。.
  • Sensor:Sensor是一种特殊类型的Operator,被设计用来等待某种信号,比如等待一 段时间过去,等待一个文件生成。在得到信号后,Sensor Task完成。
  • TaskFlow。TaskFlow是Airflow自2.0版本开始提供的全新功能。只要简单地在普通的 Python方法前加上@task装饰器,Airflow就会自动把这个方法转成Task。从功能上 来说,TaskFlow相当于PythonOperator。只不过,TaskFlow封装了很乡Airfow的功 能和特性,一方面降低了使用门槛,让用户可以在不了解这些功能和特性的情况下快 速上手编写DAG;另一方面使得DAG的代码更加简洁、优雅。

1.1 Operator

使用Operator创建Task很简单,调用Operator的构造器即可。Airflow社区有大量的 Operator,覆盖了各种各样的功能。一般情况下,用户需要做的仅仅是选择合适的Operator。 当然,如果社区提供的Operator不能满足要求,自定义Operator是最终的解决方案。下面 我们先介绍经典的Operator的用法,再介绍自定义Operator的编写方法。

1.1.1 BashOperator

BashOperator是用来执行Bash命今的Operator。代码清单4-I0提供了BashOperator的 个示例 代码清单4-10使用BashOperator创建Task

from airflow.operators.bash import Bashoperator
t1 = Bashoperator(task_id='print_date',bash_command='date')

代码清单4-10使用BashOperator构造了一个task_id为print_date的Task,这个Task在 运行时会执行Bash命令date来输出当前的时间。

1.1.2 PythonOperator

PythonOperator是用来执行Python代码的Operator。

from airflow.operators.python import PythonOperator

def print_context(**kwargs):
    print(kwargs)
t1 = PythonOperator(task_id="print_context", python_callable=print_context)

1.1.3 EmailOperator

是用来发邮件的 Operator

from airflow.operators.email import Email0perator
t1 = Emailoperator(
task_id='send_email'
to="gomin@example.com",
subject="Some subject"
html_content="Some content'
)

1.1.4 自定义Operator

所有的 Operator 都继承 自 BaseOperator 类。BaseOperaor 是一个抽象类,它的execute方 法是抽象的,由子类负责提供具体的实现。

from airflow.models.baseoperator import BaseOperator
class HelloOperator(BaseOperator):
    def __init__(self, name: str, **kwargs) -> None:
        super().__init__(**kwargs)
        self.name = name
    def execute(self, context):
        print("Hello, %s!" % self.name)

如何使用这个自动以的Operator呢?我们将代码保存到文件hello_operator.py中。下面代码就是调用方法:

from airflow import DAG
from airflow.utils.dates import days_ago
from hello_operator import HelloOperator

with DAG("how_to_use", start_date=days_ago(2)) as dag1:
    hello_task = HelloOperator(task_id="hello_world", name="world")

2 Sensor

2.1 为什么需要 Sensor

Sensor 内部实现了check和sleep的逻辑、用户只需要关心两件事情:使用哪个Senser,以及配置 Sensor 的参数。

Sensor的参数主要有3个:poke_interval、timeout和mode。 poke_interval定义了两次check之间的间隔时间。timcout定义了一直check不成功情况下的运行时间上界、超时则必须退出,释放资源。mode有两种:poke mode和reschedule mode。

2.2 常用的 Sensor

Airfiow 预先实现了非常多的 Sensor, 比较常用的有如下这些。

  • FileSensor: 等待文件系统上的一个文件/ 目录就绪。
  • S3KeySensor: 等待 S3 存储中的一个 key 出现。
  • SqlSensor: 重复运行一条 SQL 语句直到满足条件。
  • HivePartitionSensor: 等待 Hive 中的一个分区出现。
  • ExternalTaskSensor: 等待另一个 DAG Run 整体或者其中的某个 Task Instance 完成。
  • DateTimeSensor: 等到某个指定的时间。
  • TimeDeltaSensor: 一般情况下 Task 会在 (execution_date + schedule_interval) 之后运行、 TimeDeltaSensor 能够让 Task 再等待一段指定的时间。

2.3 Sensor 的工作模式

Sensor 有两种工作模式。第一种工作模式是 poke mode, 这也是默认的模式。在这种模式下, Sensor会一直运行,也就会一直占用资源,直到成功返回或者超时。在两次 check的间隙, 实际上 Sensor 是不做任何事情的,如果这时还占用资源,会造成一定程度的浪费。对于条件很快就能满足的场景, 因为 check 很快就会成功返回,所以浪费是可以被接受的。但是,对于可能很久都不能满足条件的场景、使用poke mode 会造成非常严重的资源浪费。

通过调整timeout到一个比较小的值、可以在一定程度上解决这个问题。在达到timeout指定的时间后,Sensor会退出,释放资源。但是,timeout调得很小就失去了使用Sensor的意义,针对可能需要长时间等待的条件、,更好的解决方案是他用第二种工作模式reschedule mode。

既然在两次check的间隙Sensor是不做任何事情的,那么不如把这段时间的资派释放,让别的Task使用,这正是reschedule mode的设计理念。如果一个Task是Sensor类型的并且Sensor被配置成以reschedule mode运行,那么这个Task在chceck失败后会进入up_for_reschedule状态。等待poke_interval时间后才会再次被调度运行。当Task处于up_for_reschedule状态时,它所占用的资源会被释放、从而被其他Task使用。

3 TaskFlow

使用Operator是创建Task的通用方法 ( Sensor本质上也是Operator), 但是在Task基本上都是 Python 函数的场景中、有一种更简洁和友好的方法:TaskFlow。接下来首先道过对比 PythonOperator 和 TaskFlow 在构建同样的工作流时的代码、 充分展示 TaskFlow 的优 勢。然后阐述 TaskFlow 中的两个重要概念:结果传递和依赖推断。最后介绍 TaskrFlow 对 Virtual Environment 的支持。

3.1 PythonOperator 与 TaskFlow

TaskFlow是Airflow 2.0 引人的新功能,旨在优化Task是Python函数的场景。在这之前,如果用 Python 函数作为Task, 通常都是用PythonOperator来包装的。TaskFlo 能够让 Python函数自动变成Airfiow的 Task, 只需要如下额外的两行代码。

  • 在函数前使用 @task 装饰器。
  • 调用函数生成 Task对象

通过 TaskFlow, 用户能够专注于Python函数的编写、而无须关心Airfow底层的细节。

执行下面的Python函数:

def extract(): 
    data_string = '("1001": 301.27, "1002": 433,21, "1003": 502.22)'
    order_data_dict = json.loads(data_string) 
    return order_data_dict 
    
def transform(order_data_dict: dict) :
    total order_value = 0 
    for value in order_data_dict.values(): 
        total_order_value += value 
    return ("total order value": total order value) 
 
 def load(total_order_value: float) : 
     print(f"Total_order_value is: {total_order_value:.2f}")

基于 PythonOperator 创建DAG

import json 
from airflow.decorators import dag 
from airflow.operators.python import PythonOperator 
from airflow.utils.dates import days_ago 

default_args = {owner': 'airflow'} 

@dag(default_args=default_args, schedule_interval=None, start_date=days_ago(2)) 
def tutorial_et1(): 
    def extract(**kwargs) : 
        ti = kwargg['ti'] 
        data_string = '{"1001": 301.27, "1002": 433.21, "1003": 502.22}'
        ti.xcom_push('order_data', data_string) 
        
    def transform(**kwargs): 
        ti = kwargs['ti'] 
        extract_data_string = ti.xcom_pull(task_ida='extract', key='order_data')
        order_data = json.loads(extract_data_string) 
    
        total_order_value = 0 
        for value in order_data.values():
            total_order_value += value
    
        total_value = {"total_order_value": total_order_value}
        total_value_json_string = json.dumps(total_value)
        ti.xcom_push('total_order_value', total_value_json_string)
    
    def load(**kwargs):
        ti = kwargs['ti']
        total_value_string = ti.xcom_pull(task_ids='transform', key='total_order_value')
        total_order_value = json.loads(total_value_string)
        print(total_order_value)
        
    extract_task = PythonOperator(
        task_id='extract',
        python_callable=extract
    )
    
    transform_task = PythonOperator(
        task_id='transform',
        python_callable=transform
    )
    
    load_task = PythonOperator(
        task_id='load',
        python_callable=load
    )
    
    extract_task >> transform_task >> load_task
    
tutorial_etl_etl_dag = tutorial_etl()

基于TaskFlow创建DAG的代码如下:

import json
from airflow.decorators import dag, task
from airflow.utils.dates import days_ago

default_args = {
    'owner': 'airflow'
}