我正在使用requests-threads运行以下命令:
def perform_requests():
prepared_requests = [...]
session = AsyncSession(n=100)
results = []
async def _perform_requests():
for request in prepared_requests:
results.append(session.request(**request))
for i, result in enumerate(results):
results[i] = await asyncio.ensure_future(results[i])
session.run(_perform_requests)
return results
但是,当我运行它时,会发生一些奇怪的事情,首先我会收到大量消息,例如:
(WARNING) Connection pool is full, discarding connection:
其次我得到这个错误:
results[i] = await asyncio.ensure_future(results[i])
builtins.AssertionError: yield from wasn't used with future
我正在使用ensure_future()
,这是怎么回事?
答案 0 :(得分:1)
session.request()
方法返回一个扭曲的Deferred
对象(requests-threads
代码calls twisted.internet.threads.deferToThread()
)。通常,您不想将其视为异步任务,而不是在Twisted反应堆下运行。
相反,您可以使用twisted.internet.deferred.gatherResults()
来同时执行请求并收集响应。
接下来,session.run()
调用twisted.internet.task.react()
,这将总是退出Python :
[...]此功能还将:
[...]
- 完成后退出应用程序,成功则退出代码为0,失败则退出代码。
(强调粗体的人)。
这意味着即使您的代码有效,也不会到达return results
行。
如果将session.run()
调用移出,成为应用程序的顶级入口点,那么事情就起作用了:
from requests_threads import AsyncSession
from twisted.internet import defer
session = AsyncSession(n=100)
async def perform_requests():
prepared_requests = [...]
requests = [session.request(**request) for request in prepared_requests]
responses = await defer.gatherResults(requests)
print(responses)
session.run(perform_requests)
但在打印responses
列表后立即退出。
否则,您将必须直接管理扭曲反应堆(使用reactor.run()
和响应完成后调用reactor.stop()
的回调);例如:
from requests_threads import AsyncSession
from twisted.internet import defer, error, reactor
def perform_requests():
prepared_requests = [...]
session = AsyncSession(n=100)
results = []
async def gather_responses():
requests = [session.request(**request) for request in prepared_requests]
results[:] = await defer.gatherResults(requests)
try:
reactor.stop()
except error.ReactorNotRunning:
pass
deferred = defer.ensureDeferred(gather_responses())
reactor.run()
return results
print(perform_requests())
如果您需要在扭曲的反应堆上运行多个任务,则可以使用一个顶级函数,并依靠回调函数在响应完成时通知您。
我个人认为,使用aiohttp.client
module在Python asyncio
事件循环下运行异步请求会更好:
import asyncio
import aiohttp
async def perform_requests():
prepared_requests = [...]
conn = aiohttp.TCPConnector(limit=100)
with aiohttp.ClientSession(connector=conn) as session:
requests = [session.request(**request) for request in prepared_requests]
responses = await asyncio.gather(*requests)
print(responses)
if __name__ == '__main__':
asyncio.run(perform_requests())
请注意,asyncio.run()
需要Python 3.7或更高版本;您的错误消息表明您仍在使用3.5或3.6。 work-around would be to use loop.run_until_complete()
:
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(perform_requests())