【Python入門】50.異步IO之 asyncio實現(xiàn)異步操作

摘要:如何通過asyncio實現(xiàn)異步IO坑填;用aiohttp模塊編寫支持多用戶高并發(fā)的服務器。


*寫在前面:為了更好的學習python弛姜,博主記錄下自己的學習路程脐瑰。本學習筆記基于廖雪峰的Python教程,如有侵權廷臼,請告知刪除苍在。歡迎與博主一起學習Pythonヽ( ̄▽ ̄)? *


目錄

異步IO
asyncio
async/await
aiohttp
小結

異步IO

asyncio

asyncio是Python 3.4版本引入的標準庫绝页,直接內置了對異步IO的支持。

我們只要從asyncio模塊中獲取一個EventLoop的引用寂恬,然后把需要執(zhí)行的協(xié)程放到EventLoop中執(zhí)行续誉,就可以實現(xiàn)異步IO了。

我們看個簡單的例子:

import asyncio

@asyncio.coroutine
def A():
    print('Hello, A!')
    r = yield from asyncio.sleep(1)
    print('Hello again!')

@asyncio.coroutine
def B():
    print('Hello, B!')
    r = yield from asyncio.sleep(1)
    print('Hello again!')

loop = asyncio.get_event_loop()                   # 獲取EventLoop的引用
tasks = [A(),B()]                                 # 把兩個coroutine封裝起來
loop.run_until_complete(asyncio.wait(tasks))      # 把封裝好的coroutine放到EventLoop中執(zhí)行
loop.close()

語句@asyncio.coroutine是把緊接的generator標記為協(xié)程coroutine類型初肉。

語句yield from可以調用另一個generator酷鸦,并且拿取返回值(這里為None)。

語句asyncio.sleep(1)可以當作是一個耗時1秒的IO操作牙咏。

定義好coroutine之后臼隔,把它們封裝好,放入EventLoop中執(zhí)行即可妄壶。

運行結果:

Hello, B!
Hello, A!
(間隔約1秒)
Hello again!
Hello again!

可以看到coroutine A和coroutine B是并發(fā)執(zhí)行的摔握。

如果把asyncio.sleep()換成真正的IO操作,就可以實現(xiàn)多個coroutine就由一個線程并發(fā)執(zhí)行的異步IO操作了丁寄。

我們用asyncio的異步網絡連接來獲取sina氨淌、sohu和163的網站首頁(例子源自廖雪峰官網)

import asyncio

@asyncio.coroutine
def wget(host):
    print('wget %s...' % host)
    connect = asyncio.open_connection(host, 80)
    reader, writer = yield from connect
    header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
    writer.write(header.encode('utf-8'))
    yield from writer.drain()
    while True:
        line = yield from reader.readline()
        if line == b'\r\n':
            break
        print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
    # Ignore the body, close the socket
    writer.close()

loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

執(zhí)行結果:

wget www.sohu.com...
wget www.sina.com.cn...
wget www.163.com...
(等待一段時間)
(打印出sohu的header)
www.sohu.com header > HTTP/1.1 200 OK
www.sohu.com header > Content-Type: text/html
...
(打印出sina的header)
www.sina.com.cn header > HTTP/1.1 200 OK
www.sina.com.cn header > Date: Wed, 20 May 2015 04:56:33 GMT
...
(打印出163的header)
www.163.com header > HTTP/1.0 302 Moved Temporarily
www.163.com header > Server: Cdn Cache Server V2.0
...

async/await

在Python3.5版本中,引入了新語法asyncawait伊磺,讓coroutine的代碼更簡潔易讀盛正。

其中:

async用于替換之前的@asyncio.coroutine

await用于替換之前的yield from屑埋。

我們用更為簡潔的語法把上面的代碼重新編寫一下:

import asyncio

async def wget(host):
    print('wget %s...' % host)
    connect = asyncio.open_connection(host, 80)
    reader, writer = await connect
    header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
    writer.write(header.encode('utf-8'))
    await writer.drain()
    while True:
        line = await reader.readline()
        if line == b'\r\n':
            break
        print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
    writer.close()

loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

執(zhí)行結果與上面一致蛮艰。

需要注意的是,新語法是Python3.5及之后的版本使用雀彼,若是3.4及之前的版本,仍需要用之前的語法即寡。

aiohttp

在上面的例子中徊哑,asyncio用在了客戶端上。

實際上asyncio多用在服務器端上聪富,例如Web服務器莺丑。由于HTTP連接就是IO操作,通過asyncio可以實現(xiàn)多用戶的高并發(fā)支持墩蔓。

aiohttp是基于asyncio實現(xiàn)的HTTP框架梢莽。

aiohttp沒有內置,需要通過pip下載安裝:

pip install aiohttp

我們試一下用aiohttp編寫一個服務器奸披,來處理下面兩個URL:

/:首頁昏名,返回b'<h1>Index</h1>'
/hello/{name}:根據URL參數返回文本hello, %s!阵面。

代碼如下 (源自廖雪峰官網)

import asyncio

from aiohttp import web

async def index(request):
    await asyncio.sleep(0.5)
    return web.Response(body=b'<h1>Index</h1>', content_type='text/html')

async def hello(request):
    await asyncio.sleep(0.5)
    text = '<h1>hello, %s!</h1>' % request.match_info['name']
    return web.Response(body=text.encode('utf-8'), content_type='text/html')

async def init(loop):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', index)
    app.router.add_route('GET', '/hello/{name}', hello)
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)          # 創(chuàng)建TCP服務
    print('Server started at http://127.0.0.1:8000...')
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

運行之后轻局,在瀏覽器中輸入http://localhost:8000/hello/xxx洪鸭,結果如下:

aiohttp.png

小結

asyncio提供了完善的異步IO支持;

異步操作需要在coroutine中通過yield from完成仑扑;

coroutine放到asyncio提供EventLoop引用中執(zhí)行览爵,即可實現(xiàn)異步操作;

在Python3.5及之后的版本中镇饮,語法@asyncio.coroutine替換成async蜓竹,語法yield from替換成await

異步IO更多用于服務器端储藐,通過aiohttp模塊俱济,可以簡單地編寫出支持多用戶高并發(fā)的服務器。


以上就是本節(jié)的全部內容邑茄,感謝你的閱讀姨蝴。

有任何問題與想法,歡迎評論與吐槽肺缕。

和博主一起學習Python吧( ̄▽ ̄)~*

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末左医,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子同木,更是在濱河造成了極大的恐慌浮梢,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彤路,死亡現(xiàn)場離奇詭異秕硝,居然都是意外死亡,警方通過查閱死者的電腦和手機洲尊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門远豺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坞嘀,你說我怎么就攤上這事躯护。” “怎么了丽涩?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵棺滞,是天一觀的道長。 經常有香客問我矢渊,道長继准,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任矮男,我火速辦了婚禮移必,結果婚禮上,老公的妹妹穿的比我還像新娘昂灵。我一直安慰自己避凝,他們只是感情好舞萄,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著管削,像睡著了一般倒脓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上含思,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天崎弃,我揣著相機與錄音,去河邊找鬼含潘。 笑死饲做,一個胖子當著我的面吹牛,可吹牛的內容都是我干的遏弱。 我是一名探鬼主播盆均,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漱逸!你這毒婦竟也來了泪姨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤饰抒,失蹤者是張志新(化名)和其女友劉穎肮砾,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體袋坑,經...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡仗处,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枣宫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婆誓。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖也颤,靈堂內的尸體忽然破棺而出旷档,到底是詐尸還是另有隱情,我是刑警寧澤歇拆,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站范咨,受9級特大地震影響故觅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜渠啊,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一输吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧替蛉,春花似錦贯溅、人聲如沸拄氯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽译柏。三九已至,卻和暖如春姐霍,著一層夾襖步出監(jiān)牢的瞬間鄙麦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工镊折, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胯府,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓恨胚,卻偏偏與公主長得像骂因,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赃泡,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353