Apache Airflow-可在多个地方使用的自定义类

时间:2019-05-20 12:28:52

标签: python airflow

我正在使用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上),但没有用(气流开始时“断断续续”)。因此,由于它既不是操作员,也不是传感器等,因此恐怕将来(或停止)在某些特定条件下工作。

我没有找到有关如何分隔共享行为的任何准则。我发现气流导入系统非常复杂,不是很简单。我的理想解决方案是将模块commondagsoperators移到同一级别。

我也不太确定如何从文档中解释这句话: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模块都已导入?

这是实现我的目标的好方法,还是有更好的解决方案?

谢谢您的建议

1 个答案:

答案 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_fieldstemplate_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

Source