python - 如果任务失败,我如何召回“任务”?
问题描述
有make_request
向 API 发出 http 请求的函数。而且我每秒不能发出超过 3 个请求。
我做了类似的事情
coroutines = [make_request(...) for ... in ...]
tasks = []
for coroutine in coroutines:
tasks.append(asyncio.create_task(coroutine))
await asyncio.sleep(1 / 3)
responses = asyncio.gather(*tasks)
但我也不能每小时发出超过 1000 个请求。(也许,我可以延迟3600 / 1000
。)如果互联网连接丢失怎么办?我应该尝试再次提出请求。
我可以这样包装make_request
:
async def try_make_request(...):
while True:
try:
return await make_request(...)
exception APIError as err:
logging.exception(...)
在这种情况下,每秒可能会发出超过 3 个请求。
我找到了该解决方案,但我不确定这是最好的解决方案
pending = []
coroutines = [...]
for coroutine in coroutines:
pending.append(asyncio.create_task(coroutine))
await asyncio.sleep(1 / 3)
result = []
while True:
finished, pending = await asyncio.wait(
pending, return_when=asyncio.FIRST_EXCEPTION
)
for task in finished:
exc = task.exception()
if isinstance(exc, APIError) and exc.code == 29:
pending.add(task.get_coro()) # since python 3.8
if exc:
logging.exception(...)
else:
result.append(task.result())
if not pending:
break
解决方案
如果我正确理解要求,则您启动连接的频率不得超过 3.6 秒。实现此目的的一种方法是设置一个计时器,该计时器在每次启动连接时重置,并在 3.6 秒后到期,从而允许启动下一个连接。例如:
class Limiter:
def __init__(self, delay):
self.delay = delay
self._ready = asyncio.Event()
self._ready.set()
async def wait(self):
# wait in a loop because if there are multiple waiters,
# the wakeup can be spurious
while not self._ready.is_set():
await self._ready.wait()
# We got the slot and can proceed with the download.
# Before doing so, clear the ready flag to prevent other waiters
# from commencing downloads until the delay elapses again.
self._ready.clear()
asyncio.get_event_loop().call_later(self.delay, self._ready.set)
然后try_make_request
可能看起来像这样:
async def try_make_request(limiter, ...):
while True:
await limiter.wait()
try:
return await make_request(...)
exception APIError as err:
logging.exception(...)
...并且主协程可以try_make_request
并行等待:
limiter = Limiter(3600/1000)
responses = await asyncio.gather(*[try_make_request(limiter, ...) for ... in ...])
推荐阅读
- javascript - 将用户输入字符串转换为函数要访问的对象
- xml - 处理 s3 存储桶中的大量压缩 xml 文件
- python - 试图用固定在点的“_”反转字符串
- php - 在另一个 html 文件中包含 html 部分
- sql - 计算用户在平台上花费的时间
- java - 如何通过在 java 中使用 LinkedList 来解决这个问题?
- python - 如何在后台使用颤振应用程序截取屏幕截图
- vuejs2 - Sortable 的 VueDraggable 无法正常工作,并将所选项目发送到初始化时的第一个
- python - DBSCAN - 查找地理空间数据(坐标)的 Eps 和 MinPts 的最佳方法
- vue.js - Vue Storefront 入门