簡體   English   中英

協同之外的Aiohttp ClientSession

[英]Aiohttp ClientSession outside coroutine

我有一個REST API包裝器,應該在交互式Python會話中運行。 HTTP請求既可以通過自動后台線程(使用API​​包裝器),也可以由最終用戶通過交互式會話手動完成。 我試圖將所有HTTP請求管理從前一個新的每線程請求方法遷移到asyncio,但由於我無法在主線程中運行asyncio循環(它必須是免費的ad-hoc 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並在初始化ClientSession之前啟用asyncio調試模式會引發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)

但是,這種方法更加復雜/冗長,我真的不明白它是否真的有必要。

問題:

  1. 為什么在協程之外啟動ClientSession是一個問題?
  2. 隊列方法更好/更安全嗎? 如果是這樣,為什么?
  3. 我在后台線程中啟動循環的方法有什么問題嗎?

為什么在協程之外啟動ClientSession是一個問題?

這就是aiohttp的構建方式,理論上應該可以在循環之外初始化某種客戶端會話,即。 在協程之外,但不是aiohttp的構建方式。 AFAIU在引入此警告的問題中 ,這是因為a)難以測試b)它容易出錯

隊列方法更好/更安全嗎? 如果是這樣,為什么?

我不明白你想要達到什么目標,所以我不知道如何回答。 也許您遇到的問題是您嘗試在構造函數內初始化ClientSession __init__另一個班級。 在這種情況下,您應該通過創建一個輔助方法來解決該問題,該方法是一個完成類初始化的協程。 使用異步代碼時,這是已知的模式。

我在后台線程中啟動循環的方法有什么問題嗎?

這完全沒問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM