Python之AsyncIO大雜燴
asyncio
是Python官方的協程庫,用於實現非同步IO
1. 服務端同步IO示例
from wsgiref.simple_server import make_server from time import sleep def app(_, rsp): sleep(1) rsp("200 OK", []) yield "Hello World".encode() make_server("0.0.0.0", 8888, app).serve_forever()
ApacheBench四併發測試結果
測試命令:ab -c 4 -n 4 localhost:8888/
Concurrency Level:4 Time taken for tests:4.017 seconds Complete requests:4 Failed requests:0 Total transferred:500 bytes HTML transferred:44 bytes Requests per second:1.00 [#/sec] (mean) Time per request:4017.108 [ms] (mean) Time per request:1004.277 [ms] (mean, across all concurrent requests) Transfer rate:0.12 [Kbytes/sec] received Connection Times (ms) minmean[+/-sd] medianmax Connect:000.100 Processing:1004 1758 960.320093011 Waiting:1004 1757 960.520093011 Total:1005 1758 960.320103011
單請求耗時1秒,4個併發請求耗時4秒。系統的併發處理能力為1。
time.sleep
會鎖執行緒。由於GIL全域性直譯器鎖的存在,sleep會阻塞掉整個Python直譯器。即一個請求的阻塞,會阻塞掉整個伺服器,這樣的系統沒有任何併發能力。
2. 服務端非同步IO示例
WSGI是為同步服務設計的,在非同步場景下,不能直接使用WSGI。我們可以使用第三方封裝好的aiohttp 做非同步伺服器。
from aiohttp import web import asyncio async def handle(request: web.Request): name = request.match_info.get("name", "Anonymous") text = "Hello, " + name await asyncio.sleep(1) return web.Response(text=text) app = web.Application() app.add_routes([ web.get("/", handler=handle), web.get("/{name}", handler=handle), ]) web.run_app(app, port=8888)
ApacheBench四併發測試結果
測試命令:ab -c 4 -n 4 localhost:8888/
Concurrency Level:4 Time taken for tests:2.016 seconds Complete requests:4 Failed requests:0 Total transferred:668 bytes HTML transferred:64 bytes Requests per second:1.98 [#/sec] (mean) Time per request:2016.458 [ms] (mean) Time per request:504.115 [ms] (mean, across all concurrent requests) Transfer rate:0.32 [Kbytes/sec] received Connection Times (ms) minmean[+/-sd] medianmax Connect:000.100 Processing:1008 10080.110081008 Waiting:1007 10070.410081008 Total:1008 10080.110081009
使用非同步IO後,每個請求耗時都是1秒。asyncio.sleep
不會阻塞執行緒。
3. 非同步Web框架Sanic示例
安裝:pip install sanic
from sanic import Sanic from sanic.response import json from asyncio import sleep app = Sanic() @app.route("/") async def handle(_): await sleep(1) return json({'hello': 'world'}) app.run()
控制檯輸出示例
[2019-03-19 22:49:36 +0800] [60436] [INFO] Goin' Fast @ http://127.0.0.1:8000 [2019-03-19 22:49:36 +0800] [60436] [INFO] Starting worker [60436] [2019-03-19 22:49:40 +0800] - (sanic.access)[INFO][127.0.0.1:64966]: GET http://localhost:8000/200 17 [2019-03-19 22:49:41 +0800] - (sanic.access)[INFO][127.0.0.1:64967]: GET http://localhost:8000/200 17 [2019-03-19 22:49:41 +0800] - (sanic.access)[INFO][127.0.0.1:64968]: GET http://localhost:8000/200 17 [2019-03-19 22:49:41 +0800] - (sanic.access)[INFO][127.0.0.1:64969]: GET http://localhost:8000/200 17
4. 非同步Web框架Tornado示例
from tornado.web import Application, RequestHandler from tornado.ioloop import IOLoop from asyncio import sleep class MainHandler(RequestHandler): async def get(self): await sleep(1) self.write("Hello World") Application([("/", MainHandler)]).listen(8888) IOLoop.current().start()