[英]Multiple Await in Python Async Function
我正在自定义类中使用aiohttp会话以及信号量:
async def get_url(self, url):
async with self.semaphore:
async with self.session.get(url) as response:
try:
text_response = await response.text()
read_response = await response.read()
json_response = await response.json()
await asyncio.sleep(random.uniform(0.1, 0.5))
except aiohttp.client_exceptions.ContentTypeError:
json_response = {}
return {
'json': json_response,
'text': text_response,
'read': read_response,
'status': response.status,
'url': response.url,
}
我有两个问题:
在一个异步函数中包含多个await语句是否正确/不正确? 我需要同时返回response.text()和response.read()。 但是,根据URL的不同,response.json()可能可用或可能不可用,因此我将所有内容都放入try / except块中以捕获此异常。
由于我正在使用此功能遍历不同的RESTful API端点列表,因此我通过信号量(同时设置为最大值100)来控制同时请求的数量,但是我还需要错开请求,以免它们被日志阻塞主机。 因此,我认为我可以通过添加一个在0.1-0.5秒之间随机选择的asyncio.sleep来完成此操作。 这是在两次请求之间执行小的等待的最佳方法吗? 我应该将其移动到函数的开头而不是结尾吗?
就您所知道的等待而言,在一个异步函数中具有多个等待是绝对好的,就像非常正常的顺序执行一样,每个等待都是一个接一个的等待。 关于aiohttp的一件事是,最好先调用read()
并捕获UnicodeDecodeError
,因为内部的text()
和json()
调用read()
并处理其结果,所以您不希望该处理防止至少返回read_response
。 您不必担心read()
会被多次调用,它只是在第一次调用时被缓存在响应实例中。
随机交错是突发流量的一种简单有效的解决方案。 但是,如果您想精确控制任意两个请求之间的最小时间间隔-出于学术原因,您可以设置两个信号灯:
def __init__(self): # something else self.starter = asyncio.Semaphore(0) self.ender = asyncio.Semaphore(30)
然后更改get_url()
以使用它们:
async def get_url(self, url): await self.starter.acquire() try: async with self.session.get(url) as response: # your code finally: self.ender.release()
由于starter
初始化为零,因此所有get_url()
协程将在starter
阻塞。 我们将使用单独的协程对其进行控制:
async def controller(self): last = 0 while self.running: await self.ender.acquire() sleep = 0.5 - (self.loop.time() - last) # at most 2 requests per second if sleep > 0: await asyncio.sleep(sleep) last = self.loop.time() self.starter.release()
而且您的主程序应如下所示:
def run(self): for url in [...]: self.loop.create_task(self.get_url(url)) self.loop.create_task(self.controller())
因此,起初,控制器将在15秒内均匀释放starter
30次,因为这是ender
的初始值。 在此之后,控制器将释放starter
,只要任何get_url()
结束,如果0.5秒自上次发布过starter
,或者它会等待到那个时候。
这里的一个问题是:如果要获取的URL不是内存中的恒定列表(例如,来自网络的持续不断的URL之间存在不可预测的延迟),则RPS限制器将失败(在实际上没有要获取的URL之前发布的启动程序为时过早)。 尽管这种情况下流量突发的可能性已经很小,但您仍需要进一步调整。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.