记一次 aiohttp 踩坑

闲来无事准备写个 aio 版文件下载器,中间没有碰到什么问题,但调试的时候发现发送多次请求,发一次之后就会报 Connector is closed 异常。

这里写了个 Downloader 类,实现生产者/消费者模型的下载器:

class Downloader(object):
      def __init__(self, session=None, maxsize=120):
            self.url_queue = asyncio.Queue(maxsize)
            if session:
                  self.session = session
            else:
                  self.session = asyncio.get_event_loop().run_until_complete(self.create_session())

      # 把 session 包在协程里返回,如果直接创建 session 会报个 warning
      async def create_session(self):
            return aiohttp.ClientSession()

注意这里我们通过asyncio.get_event_loop().run_until_complete(self.create_session())获取到了 session,发请求的时候直接用 self.session.get 等方法即可,问题也是出现在这里,于是调了(猜了)很多次终于发现了问题代码:

# with 会在代码执行完后关闭 session
async with self.session as session:
      async with session.request(method, url, headers, ...):
            # ...

这里习惯性的用了with语句,当这个块执行一次之后 session 就被关闭了,所以导致了之前提到的问题。
正确的写法是:

# 直接用在构造函数里创建的 session 即可
async with self.session.request(method, url, headers, ...):
      # ...

最后附上另一种复用 session 的方法——函数传参:

async with aiohttp.ClientSession() as session:
      # 把 ClientSession 示例传入协程函数复用
      await task(session)
      # ...
posted @ 2020-12-13 13:47  CTRN43062  阅读(598)  评论(0)    收藏  举报