【问题标题】:airflow http callback sensor气流http回调传感器
【发布时间】:2018-07-27 21:57:45
【问题描述】:

我们的气流实现发送 http 请求以获取服务来执行任务。我们希望这些服务在完成任务时让气流知道,因此我们向服务发送回调 url,他们将在任务完成时调用该服务。但是,我似乎找不到回调传感器。人们通常如何处理这个问题?

【问题讨论】:

    标签: callback airflow


    【解决方案1】:

    Airflow 中没有回调或 webhook 传感器之类的东西。传感器定义来自文档:

    传感器是一种特定类型的运算符,它将一直运行直到满足特定条件。示例包括 HDFS 或 S3 中的特定文件登陆、Hive 中出现的分区或一天中的特定时间。传感器从 BaseSensorOperator 派生,并在指定的 poke_interval 处运行 poke 方法,直到它返回 True。

    这意味着传感器是在外部系统上执行轮询行为的操作员。从这个意义上说,您的外部服务应该有一种方法来保持每个已执行任务的状态 - 无论是内部还是外部 - 以便轮询传感器可以检查该状态。

    通过这种方式,您可以使用例如airflow.operators.HttpSensor 轮询HTTP 端点直到满足条件。或者更好的是,编写您自己的自定义传感器,让您有机会进行更复杂的处理并保持状态。

    否则,如果服务在存储系统中输出数据,您可以使用例如轮询数据库的传感器。我相信你明白了。

    我附上了我为与 Apache Livy API 集成而编写的自定义运算符示例。传感器做两件事:a) 通过 REST API 提交 Spark 作业,b) 等待作业完成。

    运算符扩展了SimpleHttpOperator,同时实现了HttpSensor,从而结合了这两个功能。

    class LivyBatchOperator(SimpleHttpOperator):
    """
    Submits a new Spark batch job through
    the Apache Livy REST API.
    
    """
    
    template_fields = ('args',)
    ui_color = '#f4a460'
    
    @apply_defaults
    def __init__(self,
                 name,
                 className,
                 file,
                 executorMemory='1g',
                 driverMemory='512m',
                 driverCores=1,
                 executorCores=1,
                 numExecutors=1,
                 args=[],
                 conf={},
                 timeout=120,
                 http_conn_id='apache_livy',
                 *arguments, **kwargs):
        """
        If xcom_push is True, response of an HTTP request will also
        be pushed to an XCom.
        """
        super(LivyBatchOperator, self).__init__(
            endpoint='batches', *arguments, **kwargs)
    
        self.http_conn_id = http_conn_id
        self.method = 'POST'
        self.endpoint = 'batches'
        self.name = name
        self.className = className
        self.file = file
        self.executorMemory = executorMemory
        self.driverMemory = driverMemory
        self.driverCores = driverCores
        self.executorCores = executorCores
        self.numExecutors = numExecutors
        self.args = args
        self.conf = conf
        self.timeout = timeout
        self.poke_interval = 10
    
    def execute(self, context):
        """
        Executes the task
        """
    
        payload = {
            "name": self.name,
            "className": self.className,
            "executorMemory": self.executorMemory,
            "driverMemory": self.driverMemory,
            "driverCores": self.driverCores,
            "executorCores": self.executorCores,
            "numExecutors": self.numExecutors,
            "file": self.file,
            "args": self.args,
            "conf": self.conf
        }
        print payload
        headers = {
            'X-Requested-By': 'airflow',
            'Content-Type': 'application/json'
        }
    
        http = HttpHook(self.method, http_conn_id=self.http_conn_id)
    
        self.log.info("Submitting batch through Apache Livy API")
    
        response = http.run(self.endpoint,
                            json.dumps(payload),
                            headers,
                            self.extra_options)
    
        # parse the JSON response
        obj = json.loads(response.content)
    
        # get the new batch Id
        self.batch_id = obj['id']
    
        log.info('Batch successfully submitted with Id %s', self.batch_id)
    
        # start polling the batch status
        started_at = datetime.utcnow()
        while not self.poke(context):
            if (datetime.utcnow() - started_at).total_seconds() > self.timeout:
                raise AirflowSensorTimeout('Snap. Time is OUT.')
    
            sleep(self.poke_interval)
    
        self.log.info("Batch %s has finished", self.batch_id)
    
    def poke(self, context):
        '''
        Function that the sensors defined while deriving this class should
        override.
        '''
    
        http = HttpHook(method='GET', http_conn_id=self.http_conn_id)
    
        self.log.info("Calling Apache Livy API to get batch status")
    
        # call the API endpoint
        endpoint = 'batches/' + str(self.batch_id)
        response = http.run(endpoint)
    
        # parse the JSON response
        obj = json.loads(response.content)
    
        # get the current state of the batch
        state = obj['state']
    
        # check the batch state
        if (state == 'starting') or (state == 'running'):
            # if state is 'starting' or 'running'
            # signal a new polling cycle
            self.log.info('Batch %s has not finished yet (%s)',
                          self.batch_id, state)
            return False
        elif state == 'success':
            # if state is 'success' exit
            return True
        else:
            # for all other states
            # raise an exception and
            # terminate the task
            raise AirflowException(
                'Batch ' + str(self.batch_id) + ' failed (' + state + ')')
    

    希望对你有所帮助。

    【讨论】:

    • 感谢 spilio 这个有趣的回答。我正在研究使用 livy 将火花作业与气流集成。我的想法完全相同,今天我找到了你的答案。但我还没有尝试过。你能告诉我一些关于气流/livy/spark stack的经验吗?你有没有发现什么重大问题,因为我看到 livy 的开发进度没有那么快? livy 是否已经足够稳定满足您的要求?老实说,我对 livy 很陌生,但一段时间以来一直将气流用于火花以外的目的。您的任何回复都会有很大帮助。谢谢!
    • 嗨@Srikanth。到目前为止,我对 Apache Livy 的体验非常顺利,因为它通过非常简单易用的 REST API 封装了作业提交任务。在使用 Livy 之前,我必须使用标准 CLI 命令将 Spark 作业提交到集群,这要求 Spark 二进制文件在客户端计算机上可用。由于我使用的是 Hortonworks Data 平台,因此只需通过 Ambari 单击一下即可将 Livy 添加到集群中。最重要的是,它可以应用集群中配置的任何安全元素。
    • 此外,通过 Livy 提交作业本质上是异步的,允许您执行非阻塞 Airflow 任务。这非常有用,因为您可以让不同类型的操作员等待作业完成 - 可以是提交/轮询操作员,例如我分享的同时执行两项作业的操作员,也可以是等待作业完成然后继续执行的仅轮询操作员其他任务。这可以为您的流程提供新的动力,并以非常有用的方式解耦事物。希望这会有所帮助:)
    • 感谢您的宝贵意见 spilio。它确实有助于做出有利于气流+livy+spark的决定。
    • 谢谢@spilio。真的很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    相关资源
    最近更新 更多