我正在使用Airflow 1.10.2。而且我正在尝试定义一个自定义模块,该模块将包含可在多个dag和运算符中使用的常规功能。
一个具体示例可以是enum
。我想在自定义运算符中使用它(以修改其行为)。但是我也想在dag
定义中使用它,并将其用作参数。
这是我当前的层次结构
airflow_home
| - dags/
- __init__.py
- my_dag.py
| - plugins/
- operators/
- __init__.py
- my_operator.py
- common/
- __init__.py
- my_enum.py
假设我要定义一个枚举(在my_enum.py
模块中):
class MyEnum(Enum):
OPTION_1 = 1
OPTION_2 = 2
它以以下方式导入到运算符(在my_operator.py
中):
from common.my_enum import MyEnum
以同样的方式进入(my_dag.py
):
from common.my_enum import MyEnum
奇怪地(?),这对我有用。但是,我不确定这是否是正确的方法。一位同事告诉我,他过去曾尝试这样做(可能是在较旧版本的Airflow上),但没有用(气流开始时“断断续续”)。因此,由于它既不是操作员,也不是传感器等,因此恐怕将来(或停止)在某些特定条件下工作。
我没有找到有关如何分隔共享行为的任何准则。我发现气流导入系统非常复杂,不是很简单。我的理想解决方案是将模块common
与dags
和operators
移到同一级别。
我也不太确定如何从文档中解释这句话:The python modules in the plugins folder get imported, and hooks, operators, sensors, macros, executors and web views get integrated to Airflow’s main collections and become available for use.
是否表示我的方法是正确的,因为plugins/
中的任何python模块都已导入?
这是实现我的目标的好方法,还是有更好的解决方案?
谢谢您的建议
答案 0 :(得分:0)
这有点像做事的方式。
正确的方法是首先创建一个 hook
而不是 operator
它将使用这个钩子。对于下面的更简单的情况,您甚至不需要在运算符中调用钩子。
#1。 放置
<PROJECT NAME>/<PLUGINS_FOLDER>/<PLUGIN NAME>/__init__.py
<PROJECT NAME>/<PLUGINS_FOLDER>/<PLUGIN NAME>/<some_new>_hook.py
<PROJECT NAME>/<PLUGINS_FOLDER>/<PLUGIN NAME>/<some_new>_operator.py
对于看起来像这样的真实案例场景:
CRMProject/crm_plugin/__init__.py
CRMProject/crm_plugin/crm_hook.py
CRMProject/crm_plugin/customer_operator.py
#2。 代码
CRMProject/crm_plugin/__init__.py
的示例代码:
# CRMProject/crm_plugin/__init__.py
from airflow.plugins_manager import AirflowPlugin
from crm_plugin.crm_hook import CrmHook
from crm_plugin.customer_operator import CreateCustomerOperator, DeleteCustomerOperator, UpdateCustomerOperator
class AirflowCrmPlugin(AirflowPlugin):
name = "crm_plugin" # does not need to match the package name
operators = [CreateCustomerOperator, DeleteCustomerOperator, UpdateCustomerOperator]
sensors = []
hooks = [CrmHook]
executors = []
macros = []
admin_views = []
flask_blueprints = []
menu_links = []
appbuilder_views = []
appbuilder_menu_items = []
global_operator_extra_links = []
operator_extra_links = []
钩子类的示例代码 - CRMProject/crm_plugin/crm_hook.py
。永远不要直接从 system\API 调用它。为此使用运算符(见下文)。
from airflow.hooks.base_hook import BaseHook
from airflow.exceptions import AirflowException
from crm_sdk import crm_api # import external libraries to interact with target system
class CrmHook(BaseHook):
"""
Hook to interact with the ACME CRM System.
"""
def __init__(self, ...):
# your code goes here
def insert_object(self, ...):
"""
Insert an object into the CRM system
"""
# your code goes here
def update_object(self, ...):
"""
Update an object into the CRM system
"""
# your code goes here
def delete_object(self, ...):
"""
Delete an object into the CRM system
"""
# your code goes here
def extract_object(self, ...):
"""
Extract an object into the CRM system
"""
# your code goes here
您将在 DAG 中使用的运算符 (CRMProject/crm_plugin/customer_operator.py
) 的示例代码。运算符要求您实现一个 execute 方法。这是 Airflow 操作符的入口点,它会在 DAG 中的任务执行时调用。
apply_defaults
装饰器包装了类的 __init__
方法,该方法在运行时将 DAG 脚本中设置的 DAG 默认值应用于操作员的任务实例。
我们还可以设置两个重要的类属性。它们是 templated_fields
和 template_ext
。这两个属性是可迭代对象,应包含字段和/或文件扩展名的字符串值,允许使用 Airflow 中的 jinja 模板支持进行模板化。
from airflow.exceptions import AirflowException
from airflow.operators import BaseOperator
from airflow.utils.decorators import apply_defauls
from crm_plugin.crm_hook import CrmHook
class CreateCustomerOperator(BaseOperator):
"""
This operator creates a new customer in the ACME CRM System.
"""
template_fields = ['first_contact_date', 'bulk_file']
template_ext = ['.csv']
@apply_defaults
def __init__(self, first_contact_date, bulk_file, ...):
# your code goes here
def _customer_exist(self, ...):
"""
Helper method to check if a customer exist. Raises an exception if it does.
"""
# your code goes here
def execute(self, context):
"""
Create a new customer in the CRM system.
"""
# your code goes here
您可以根据需要在类中创建任意数量的方法,以简化执行方法。良好的类设计的相同原则在这里仍然很重要。
#3。 部署和使用您的插件
完成插件的工作后,剩下要做的就是将 <PLUGIN NAME>
包文件夹复制到 Airflow 插件文件夹。 Airflow 会选择插件,并且它可以被你的 DAG 使用。
如果我们将简单的 CRM 插件复制到我们的 plugins_folder
中,文件夹结构将如下所示。
<plugins_folder>/crm_plugin/__init__.py
<plugins_folder>/crm_plugin/crm_hook.py
<plugins_folder>/crm_plugin/customer_operator.py
为了使用您的新插件,您只需使用以下语句导入您的 Operators 和 Hooks。
from airflow.hooks.crm_plugin import CrmHook
from airflow.operators.crm_plugin import CreateCustomerOperator, DeleteCustomerOperator, UpdateCustomerOperator