摘要:如何通過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版本中,引入了新語法async
和await
伊磺,讓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
洪鸭,結果如下:
小結
asyncio
提供了完善的異步IO支持;
異步操作需要在coroutine
中通過yield from
完成仑扑;
把coroutine
放到asyncio
提供EventLoop
引用中執(zhí)行览爵,即可實現(xiàn)異步操作;
在Python3.5及之后的版本中镇饮,語法@asyncio.coroutine
替換成async
蜓竹,語法yield from
替換成await
。
異步IO更多用于服務器端储藐,通過aiohttp
模塊俱济,可以簡單地編寫出支持多用戶高并發(fā)的服務器。
以上就是本節(jié)的全部內容邑茄,感謝你的閱讀姨蝴。
有任何問題與想法,歡迎評論與吐槽肺缕。
和博主一起學習Python吧( ̄▽ ̄)~*