項目中需要用到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()