1. 程式人生 > 實用技巧 >記一次 aiohttp 踩坑

記一次 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)
      # ...