【问题标题】:How do I implement synchronously waiting for a callback in Python 3 asyncio?如何在 Python 3 asyncio 中实现同步等待回调?
【发布时间】:2017-06-26 04:43:58
【问题描述】:

由于一些不寻常的限制,我需要在返回响应之前同步等待来自另一个服务的回调 URL。目前我有类似的东西:

ROUTE = '/operation'
async def post(self):
    ##SOME OPERATIONS##
    post_body = { 'callbackUrl' : 'myservice.com/cb' }
    response = await other_service.post('/endpoint')
    global my_return_value
    my_return_value = None
    while not my_return_value:
        pass
    return self.make_response(my_return_value)

然后我有一种方法来处理回调 URL,例如:

ROUTE = '/cb'
async def post(self):
    ##OPERATIONS###
    global my_return_value
    my_return_value = some_value
    return web.json_response()

此代码的问题在于,即使调用了回调 URL,它也会永远被困在 while 循环中。我怀疑有更好的方法可以做到这一点,但我不知道如何去做,也不知道如何用谷歌搜索它。有什么想法吗?

提前致谢!

【问题讨论】:

  • 可能有更好的方法可以做到这一点,但我认为您的直接问题是您在回调路由中缺少global my_return_value
  • 那么为什么不让other_service.post() 返回一个(some_value, web.json_response()) 元组呢?
  • 你会得到一个无限循环,因为你在调用帖子后设置了my_return_value = None。您的协同程序仍然是连续的/operation post() 协同程序在 response = await other_service.post('/endpoint') 返回之前不会继续。
  • @MartijnPieters 我相信 other_service.post(...) 调用正在发出 HTTP 请求(然后返回),而在其他服务转身并向我们返回 HTTP 请求之前不会调用回调.
  • @smarx 是正确的

标签: python-3.x python-asyncio aiohttp


【解决方案1】:

只是快速扫描,但我认为你被困在里面了

while not my_return_value:
    pass

Python 会被困在那里,没有时间处理回调函数。你需要的是

while not my_return_value:
    await asyncio.sleep(1)

(或者如果您不想要毫秒延迟,您甚至可以发送asyncio.sleep(0))。

更好的方法是(现在我是凭记忆写的,不能保证......):

my_return_value = asyncio.get_event_loop().create_future()
await my_return_value
return self.make_response(my_return_value.result())


async def post(self):
    ##OPERATIONS###
    my_return_value.set_result(some_value)
    return web.json_response()

但是请注意,如果该系统有多个并发使用,则任何一种方式都会非常糟糕。感觉非常脆弱!也许更好:

ROUTE = '/operation'
my_return_value = {}

async def post(self):
    ##SOME OPERATIONS##
    token = "%016x" % random.SystemRandom().randint(0, 2**128)
    post_body = { 'callbackUrl' : 'myservice.com/cb?token='+token }
    response = await other_service.post('/endpoint')
    my_return_value[token] = asyncio.get_event_loop().create_future()
    await my_return_value[token]
    result = my_return_value[token].result()
    del my_return_value[token]
    return self.make_response(result)

async def post(self):
    ##OPERATIONS###
    token = self.arguments("token")
    my_return_value[token].set_result(some_value)
    return web.json_response()

现在,cherry on top 将是一个计时器,它会在超时后取消未来,并在一段时间后清理 my_return_value 中的条目,如果回调没有发生。另外,如果您同意我的最后一个建议,请不要将其称为 my_return_value,而是将其称为 callback_future_by_token...

【讨论】:

  • 我当前的解决方案看起来像你的第二个解决方案,除了用 uuid 代替随机数生成。不过,我对使用 Futures 没有信心,所以这特别有用。
猜你喜欢
  • 2021-10-12
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
相关资源
最近更新 更多