【问题标题】:Simulate a synchronous request on top of background async job with Flask使用 Flask 在后台异步作业之上模拟同步请求
【发布时间】:2017-03-17 15:55:06
【问题描述】:

我先解释一下我的系统的架构,然后转入问题:

我有一个 REST API,用作我的 API 网关。该服务器是使用 Flask 构建的。我也有 RabbitMQ 集群,以及我编写的一个客户端,它监听特定队列并执行其获取的任务。

到目前为止,我的所有请求都是异步的,所以一旦请求到达 API 网关,带有 URL 的 callback_uri 字段将作为请求的一部分提供结果,API 网关只负责用于将任务发送到 RabbitMQ,worker 处理任务,最后将结果 POST 回回调 URL。

我的问题是:

我希望新端点是同步的,处理仍将由与以前相同的工作人员完成,但我希望将结果返回 API 网关以返回给用户并释放连接。

我目前的解决方案:

我像以前一样将唯一的callback_uri 作为请求的一部分发送给工作人员,但现在特定端点由我的 API 网关实现,并允许 POST 和 GET 方法,因此工作人员可以发布一次结果它完成了,我的 API 网关不断轮询回调 URL,直到有结果可用,然后将结果返回给客户端。

除了让一个忙着等待的 HTTP 工作者轮询自己的端点以获取结果之外,还有其他首选选项吗?但仍然是同步的,所以只有在结果可用时才释放连接?

代码仅供说明:

@app.route('/long_task', methods=['POST'])
@sync_request
def long_task():
    try:
        if request.get_json() is None:
            return ERROR_MSG_NO_JSON, 400
        create_and_send_request_to_rabbitmq()
        return '', 200
    except Exception as ex:
        return ERROR_MSG_NO_DATA, 400


def sync_request(func):

    def call(*args, **kwargs):
        create_callback_uri()
        result = func(*args, **kwargs)
        status_code = result[1]
        if status_code == 200:
            result = get_callback_result()
        return result

    return call

def get_callback_result():
    callback_uri = request.get_json()['callback_uri']
    has_answer = False
    headers = {'content-type': 'application/json'}
    empty_response = {}
    content = json.dumps(empty_response)

    try:
        with Timeout(seconds=SYNC_REQUEST_TIMEOUT_SECONDS):
            while not has_answer:
                response = requests.get(callback_uri, headers=headers)
                if response.status_code == 200:
                    has_answer = True
                    content = response.content
                else:
                    time.sleep(0.2)
    except TimeoutException:
        log.debug('Timed out on sync request for request %s ' % request)

    return content, 200

【问题讨论】:

    标签: python-2.7 http flask rabbitmq polling


    【解决方案1】:

    因此,如果我理解正确,您希望您的后端等待某个工作人员的响应(通过 RabbitMQ)。您可以通过实现rpc over rabbitmq 来实现这一点。关键思想是使用相关 id。

    但绝对最有效的方法是通过 websockets(或原始 tcp 套接字,如果它不是浏览器)运行客户端,并在工作完成时直接通知他。这样您就不会锁定资源(客户端连接、rabbitmq 队列)并避免性能损失 (rpc)。

    【讨论】:

    • 关于 tcp sockets 解决方案,后端如何知道何时通知以及通知什么? RPC 解决方案是否仍应与 tcp 套接字解决方案一起使用?
    • @AvihooMamka RPC 解决方案可以通过经典 HTTP 实现。您只需修改您的long_task 以等待来自 RabbitMQ 的响应。至于 TCP:后端不知道何时通知。但工人知道。所以他通知后端(它可以通过经典的HTTP请求来做,它还必须知道哪个后端,所以你必须通过RabbitMQ发送额外的信息)然后后端通知客户端(后端跟踪所有连接的客户端) .不过,这可能很难实现,您必须记住边缘情况(例如随机断开连接)。
    • 我不确定我是否理解如何让客户端发送 HTTP POST 请求并且必须等到响应返回。比如,我如何将您提出的解决方案连接到发布请求的客户端 http 连接并等待与结果的相同连接?
    • @AvihooMamka 客户端发送一个请求,然后long_task 向 RabbitMQ 发送一条带有适当关联 ID 的消息。然后long_task 被锁定,直到 RabbitMQ 响应。之后long_task 响应客户端。这个“long_task被锁定”部分是我链接的文章的内容。换句话说,您可以同步地等待 RabbitMQ 响应。这是一个特点。 :) 查看FibonacciRpcClient 的代码并尝试一下。
    • 我得到了使用 RPC 的那部分,但你说有第二种方法使用原始 TCP 套接字,这对我来说有点不清楚,但也许第一种 RPC 方法更容易实现,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多