当前位置: 首页 > 知识库问答 >
问题:

异步请求回退/限制最佳实践

谯德佑
2023-03-14

场景:我需要从web应用程序的API收集分页数据,该API的调用限制为每分钟100次。我需要返回的API对象每页包含100个项目,总共105个页面,并且在不断增长(~10500个项目)。同步代码检索所有页面大约需要15分钟,因此不必担心达到调用限制。但是,我想加快数据检索速度,因此我使用asyncioaiohttp实现了异步调用。数据现在可以在15秒内下载-很好。

问题:我现在达到呼叫限制,因此在过去的5个左右的呼叫中收到403个错误。

建议的解决方案我实现了get_data()函数中的try/except。我拨打电话,然后当由于403:超出呼叫限制而导致呼叫不成功时,我会后退秒,然后重试最多

async def get_data(session, url):
    retries = 3
    back_off = 60  # seconds to try again
    for _ in range(retries):
        try:
            async with session.get(url, headers=headers) as response:
                if response.status != 200:
                    response.raise_for_status()
                print(retries, response.status, url)
                return await response.json()
        except aiohttp.client_exceptions.ClientResponseError as e:
            retries -= 1
            await asyncio.sleep(back_off)
            continue

async def main():
    async with aiohttp.ClientSession() as session:
        attendee_urls = get_urls('attendee') # returns list of URLs to call asynchronously in get_data()
        attendee_data = await asyncio.gather(*[get_data(session, attendee_url) for attendee_url in attendee_urls])
        return attendee_data

if __name__ == '__main__':
    data = asyncio.run(main())

问题:我如何限制aiohttp呼叫,使其保持在每分钟100个呼叫的阈值之下,而无需发出403请求以退出?我尝试了以下模块,但没有一个模块能起到任何作用:ratelimiterratelimiterasyncio throttle

目标:每分钟进行100次异步调用,但在必要时后退并重试(403:超出调用限制)。


共有1个答案

笪煌
2023-03-14

通过在每个请求之前添加延迟,可以实现“最多100个请求/分钟”。

100次请求/分钟相当于1次请求/0.6s。

async def main():

    async with aiohttp.ClientSession() as session:
        attendee_urls = get_urls('attendee') # returns list of URLs to call asynchronously in get_data()
        coroutines = []
        for attendee_url in attendee_urls:
            coroutines.append(get_data(session, attendee_url))
            await asyncio.sleep(0.6)
        attendee_data = asyncio.gather(*coroutines)
        return attendee_data

除了请求速率限制外,API通常还限制同时请求的数量。如果是这样,可以使用BoundedSempahore。

async def main():
    sema = asyncio.BoundedSemaphore(50) # Assuming a concurrent requests limit of 50
...
            coroutines.append(get_data(sema, session, attendee_url))
...

def get_data(sema, session, attendee_url):

...

    for _ in range(retries):
        try:
            async with sema:
                response = await session.get(url, headers=headers):
                if response.status != 200:
                    response.raise_for_status()
...
 类似资料:
  • 好的,所以我找不到关于这个主题的任何有用的材料,我找到的一大块文章中有一个方法是用@HystrixCommand注释的,并且定义了一个回退方法。 我找到的另一个解决方案是使用@DefaultProperties(defaultFallback=“fallbackMethod”),但问题是这些方法需要具有兼容的返回类型。 不幸的是,在我的服务中,我有许多具有完全不同签名的方法,我还需要掌握可抛出的方

  • 问题内容: 我最近写了我的第一个Android应用程序,大约有8,000-10,000行代码。一直阻碍我使用常规设计模式的一件事是android对异步调用(打开对话框,活动等)的大量使用。因此,我的代码很快就开始看起来像“意大利面”,而我最终开始不喜欢某些类。 是否有适用于任何人都会推荐的系统的特定设计模式或编程方法?对于编写可管理的异步代码有什么建议吗? 问题答案: 使用全局变量 如果您不想用简

  • 问题内容: 我看过很多例子,它们似乎都以不同的方式解决了这个问题。基本上,我只是想要最简单的方法来发出不会锁定主线程并且可以取消的请求。 这也无济于事,我们至少有2个HTTP库可供选择,它们分别是java.net。(例如HttpURLConnection)和org.apache.http。。 最佳做法是什么? 问题答案: Android 1.5 SDK引入了一个新类AsyncTask,该类旨在使在

  • 我试图了解如何最好地设计基于IIS/ASP.NET的websocket应用程序,特别是关于并发限制。 我已经阅读了关于“并发Websocket连接”以及如何调整各种值的所有IIS/ASP.NET文献--但是,当谈到websockets时,“并发”的定义是什么?如果我打开了一个websocket,并且它处于空闲状态,这是在“使用”连接吗?空闲websockets是否计入连接使用总数,或者只在发送/接

  • 问题内容: 我有一个ajax问题: 如果循环6次(在我的foreach循环中),我应该对服务器发出6个异步请求。但是在这种情况下,ajax调用是同步调用的,而不是异步调用的。有人知道为什么会这样吗? 问题答案: 好,谢谢。经过几个小时的分析和反思,我意识到了为什么该脚本会同步运行:我打开script.php文件,并注意到了这一点以及该文件的开头: 因此,我对使用会话的php脚本进行了并行ajax调

  • 问题内容: 所以我有以下代码循环遍历一个对象: 对于每个迭代,我要发出一个请求(发送一条Facebook消息),仅在该请求完成后,我才要进行下一个迭代,这是因为没有任何回调,消息将不会正确地连续发送。 我以前曾遇到过这个问题,但无法解决,我该怎么做? 如果您有任何问题,请询问。谢谢。 问题答案: 我最终遵循了@Matt Diamond的建议,并执行了如下所示的递归函数: 谢谢大家的帮助,不胜感激。