我有一个REST API包装器,该包装器应该在交互式Python session 中运行。 HTTP请求既可以通过自动后台线程(使用API​​包装器)发出,也可以由最终用户通过交互式 session 手动发出。我正在尝试将所有HTTP请求管理从以前的每请求一个新线程迁移到asyncio,但是由于我无法在主线程中运行asyncio循环(对于特定的Python命令,它必须是免费的/请求),我编写了以下代码以在后台线程中运行它:

import aiohttp
import asyncio
from concurrent.futures import ThreadPoolExecutor

def start_thread_loop(pool=None):
    """Starts thread with running loop, bounding the loop to the thread"""
    def init_loop(loop):
        asyncio.set_event_loop(loop)  # bound loop to thread
        loop.run_forever()
    _pool = ThreadPoolExecutor() if pool is None else pool
    loop = asyncio.new_event_loop()
    future = _pool.submit(init_loop, loop)
    return future, loop

def send_to_loop(coro, loop):
    """Wraps couroutine in Task object and sends it to given loop"""
    return asyncio.run_coroutine_threadsafe(coro, loop=loop)

实际的API包装器如下所示:
class Foo:
    def __init__(self):
        _, self.loop = start_thread_loop()
        self.session = aiohttp.ClientSession(loop=self.loop)
        self.loop.set_debug(True)

    def send_url(self, url):
        async def _request(url):
            print('sending request')
            async with self.session.get(url) as resp:
                print(resp.status)
        return send_to_loop(_request(url), self.loop)

但是,aiohttp强烈建议您不要制作ClientSession在协程外部并在初始化asyncio之前打开ClientSession Debug模式会引发RuntimeError。因此,为了避免在协程内部制作asycio.Queue,我尝试使用ClientSession制作一个稍有不同的版本:
class Bar:

    def __init__(self):
        _, self.loop = start_thread_loop()
        self.q = asyncio.Queue(loop=self.loop)
        self.status = send_to_loop(self.main(), loop=self.loop)

    async def main(self):
        async with aiohttp.ClientSession(loop=self.loop) as session:
            while True:
                url = await self.q.get()
                print('sending request')
                asyncio.ensure_future(self._process_url(url, session), loop=self.loop)

    def send_url(self, url):
        send_to_loop(self.q.put(url), loop=self.loop)

    @staticmethod
    async def _process_url(url, session):
        async with session.get(url) as resp:
            print(resp.status)

但是,这种方法更加复杂/冗长,我真的不了解它是否确实必要。

问题:
  • 为什么在协程之外启动ClientSession是一个问题?
  • 队列方法更好/更安全吗?如果是这样,为什么?
  • 我的方法在后台线程内启动循环是否有问题?
  • 最佳答案



    这就是aiohttp的构建方式,从理论上讲,应该可以在循环外部初始化某种客户端 session 。在协程外部,但这不是aiohttp的构建方式。 the issue that introduced this warning中的AFAIU,这是因为a)很难测试b)容易出错



    我不了解您要达到的目标,所以我不确定该如何回答。也许您遇到的问题是您尝试在构造函数内部又初始化ClientSession。另一个类的__init__。在这种情况下,您应该通过创建一个辅助程序来解决该问题,该辅助程序是一个协程,它将完成该类的初始化。使用异步代码时,这是已知的模式。



    没关系

    关于python - 协程外的Aiohttp ClientSession,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43108418/

    10-10 13:34