asyncio+aiohttp異步IO的簡單使用

項目中需要用到Python裏的Asyncio模塊去異步發生HTTP請求,做一個簡單的demo記錄。

Asyncio的基礎知識可以參考官網文檔,建議直接閱讀3.7+版本的文檔,之前的都太老了,網上搜的例子也很多都是3.7-的,還用的低級API。

demo功能:通過同步、異步兩種方式循環調用百度地圖API,觀察兩種方式IO耗時。

一、準備數據源

ak是百度地圖API的祕鑰,這裏準備了3個待查詢的地址。

def prepare_data():
    url = "http://api.map.baidu.com/geocoding/v3/"
    ak = "23ZRXDWDGmDRwnOfANHzB7v0hd5Hk6VC"
    params = [
        {
            "address": "北京市海淀區西土城路10號北京郵電大學",
            "output": "json",
            "ak": ak
        },
        {
            "address": "北京市西城區西長安街2號國家大劇院",
            "output": "json",
            "ak": ak
        },
        {
            "address": "北京市東城區錢糧衚衕3號東城區人民政府",
            "output": "json",
            "ak": ak
        },
    ]
    return url, params

二、同步IO

sync_do_tasks去遍歷3個查詢地址,sync_get_postion則通過request模塊去發送get請求。

def sync_get_position(url, params):
    res = requests.get(url, params=params)
    data = json.loads(res.text)
    if data['status'] == 0:
        return data['result']
    else:
        return None


def sync_do_tasks():
    url, params = prepare_data()
    results = [sync_get_position(url, p) for p in params]
    for r in results:
        print(r)

三、異步IO

同理,async_do_tasks組裝tasks任務,通過gather方法併發執行。

async_get_postion則通過aiohttp模塊去實現異步HTTP請求。

async def async_get_position(url, params):
    async with aiohttp.ClientSession() as session:
        async with session.get(url, params=params) as resp:
            return await resp.text()


async def async_do_tasks():
    url, params = prepare_data()
    tasks = [async_get_position(url, p) for p in params]
    response = await asyncio.gather(*tasks)
    for r in response:
        print(r)

四、main

在While循環中分別調用同步、異步代碼,並計時。其中異步通過asyncio.run()作爲入口。

def now():
    return time.time()
    
def main():
    loggers.init()
    logs = loggers.get(__name__)

    while True:
        try:
            logs.debug("start do sync tasks")
            print("sync task")
            sync_start = now()
            sync_do_tasks()
            sync_time = now() - sync_start
            print(sync_time)
            logs.debug(f"success to do sync tasks in {sync_time} seconds")

            logs.debug("start do async tasks")
            print("async task")
            async_start = now()
            asyncio.run(async_do_tasks())
            async_time = now() - async_start
            print(async_time)
            logs.debug(f"success to do async tasks in {async_time} seconds")
        except Exception as e:
            logs.debug("failed to do tasks")
        finally:
            time.sleep(5)

效果如下:同步、異步兩種方式都成功獲得response信息,異步耗時小於同步,效果還是比較明顯的。

全部代碼如下:

import aiohttp
import asyncio
import json
import requests
import time

import loggers


def now():
    return time.time()


def prepare_data():
    url = "http://api.map.baidu.com/geocoding/v3/"
    ak = "23ZRXDWDGmDRwnOfANHzB7v0hd5Hk6VC"
    params = [
        {
            "address": "北京市海淀區西土城路10號北京郵電大學",
            "output": "json",
            "ak": ak
        },
        {
            "address": "北京市西城區西長安街2號國家大劇院",
            "output": "json",
            "ak": ak
        },
        {
            "address": "北京市東城區錢糧衚衕3號東城區人民政府",
            "output": "json",
            "ak": ak
        },
    ]
    return url, params


def sync_get_position(url, params):
    res = requests.get(url, params=params)
    data = json.loads(res.text)
    if data['status'] == 0:
        return data['result']
    else:
        return None


def sync_do_tasks():
    url, params = prepare_data()
    results = [sync_get_position(url, p) for p in params]
    for r in results:
        print(r)


async def async_get_position(url, params):
    async with aiohttp.ClientSession() as session:
        async with session.get(url, params=params) as resp:
            return await resp.text()


async def async_do_tasks():
    url, params = prepare_data()
    tasks = [async_get_position(url, p) for p in params]
    response = await asyncio.gather(*tasks)
    for r in response:
        print(r)


def main():
    loggers.init()
    logs = loggers.get(__name__)

    while True:
        try:
            logs.debug("start do sync tasks")
            print("sync task")
            sync_start = now()
            sync_do_tasks()
            sync_time = now() - sync_start
            print(sync_time)
            logs.debug(f"success to do sync tasks in {sync_time} seconds")

            logs.debug("start do async tasks")
            print("async task")
            async_start = now()
            asyncio.run(async_do_tasks())
            async_time = now() - async_start
            print(async_time)
            logs.debug(f"success to do async tasks in {async_time} seconds")
        except Exception as e:
            logs.debug("failed to do tasks")
        finally:
            time.sleep(5)


if __name__ == '__main__':
    main()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章