簡體   English   中英

使用aiohttp的Python 3.6異步GET請求正在同步運行

[英]Python 3.6 async GET requests in with aiohttp are running synchronously

我有以下功能正常,但由於某種原因,請求似乎是同步執行,而不是異步。

我現在的假設是,這是因為main函數中的for循環for record in recordsfor record in records ,但我不知道如何更改它以便請求可以執行異步。 如果不是這樣,我還需要改變什么呢?

async def do_request(query_string):
        base_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?'
        params = {'key': google_api_key,
                  'query': query_string}
        async with aiohttp.ClientSession() as session:
            async with session.request('GET', base_url, params=params) as resp:
                return resp


async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        resp = await do_request(query_string)
        print("NOW WRITE TO DATABASE")

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

您正在等待單獨的do_request()調用。 而不是直接等待它們(在協程完成之前阻塞它們),使用asyncio.gather()函數讓事件循環同時運行它們:

async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    requests = []
    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        requests.append(do_request(query_string))

    for resp in asyncio.gather(*requests):
        print("NOW WRITE TO DATABASE")

asyncio.gather()返回值是coroutines返回的所有結果的列表,其順序與您將它們傳遞給gather()函數的順序相同。

如果您需要原始記錄來處理響應,您可以通過幾種不同的方式配對記錄和查詢字符串:

  • 將有效記錄存儲在單獨的列表中,並在處理響應時使用zip()將它們再次配對
  • 使用一個幫助程序,它獲取有效記錄,生成一個查詢字符串,調用請求,並將記錄和響應一起作為元組返回。

您還可以將響應處理混合到聚集的協程中; 一個記錄,生成查詢字符串,等待do_request ,然后在響應准備好時將結果存儲在數據庫中。

換句話說,將你需要連續發生的工作分成協同程序並收集它們。

建立Martijn的答案

如果請求的順序對您來說無關緊要(當它寫入數據庫時​​),您可以在獲取命令時將響應寫入數據庫。

編輯(解釋更多):我在這里使用2個信號量。 1是通過aiohttp限制連接數。 這取決於您的系統。 大多數Linux系統默認為1024.根據我自己的個人經驗,將其設置為低於OS max是更可取的。

max_coroutines是解決一次運行太多協程的問題。

我使用asyncio.ensure_future()以便在構建列表時運行協同程序。 這樣,在執行任何協同程序之前,您不會創建完整的協程列表。

# Limit the total number of requests you make by 512 open connections.
max_request_semaphore = asyncio.BoundedSemaphore(512)
max_coroutines = asyncio.BoundedSemaphore(10000)


async def process_response(response):
    print('Process your response to your database')


async def do_request(query_string):
    base_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?'
    params = {'key': google_api_key,
              'query': query_string}
    async with max_request_semaphore:
        async with aiohttp.ClientSession() as session:
            async with session.request('GET', base_url, params=params) as resp:
                return resp


# Excuse me for the bad function naming
async do_full_request(query_string):
    resp = await do_request(query_string)
    await process_response(resp)
    max_coroutines.release()

async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    requests = []
    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        # Will prevent more than 10k coroutines created.
        await max_coroutines.acquire()
        requests.append(
            asyncio.ensure_future(
                do_full_request(query_string)))

    # Now gather all the coroutines
    await asyncio.gather(*requests)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

暫無
暫無

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

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