使用Flask在后台异步作业的基础上模拟同步请求

时间:2017-03-17 15:55:07

标签: python-2.7 http flask rabbitmq polling

我将首先解释我的系统架构,然后转到问题:

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

到目前为止,我的所有请求都是异步的,因此一旦请求到达API网关,就会有一个callback_uri字段,其中包含要将结果作为请求的一部分POST的URL,并且API网关是负责将任务发送到RabbitMQ并且工作人员处理了任务,最后将结果发回到回调URL。

我的问题是:

我希望新端点在某种意义上是同步的,处理将由与之前相同的工作者完成,但我想将结果返回到API网关以返回给用户并释放连接。

我目前的解决方案:

我像以前一样向工作人员发送一个唯一的callback_uri作为请求的一部分,但现在特定的端点由我的API网关实现并允许POST和GET方法,因此工作人员可以POST结果一旦完成,我的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

1 个答案:

答案 0 :(得分:2)

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

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