網路非同步請求之gevent

我們知道對於網路請求這種IO bound的場景來說,最怕的就是某個請求阻塞了其餘的操作,讓並發性大大降低。今天就來介紹一款python下的並發庫-gevent。

首先看一下他自己的介紹:

gevent是一個基於libev的並發庫。它為各種並發和網路相關的任務提供了整潔的API。

嗯,確實很簡潔,很易使用。待會我們就見識到了。

同步IO

首先我們來看看傳統的IO請求。 比如我們請求:http://httpbin.org/ip 發個http get請求會得到我們的ip地址,如:

{    "origin": "183.240.202.14"  }

我們來寫個腳本連續發50個get請求到這個地址,以模擬多個請求。(腳本命名為get.py)

import requests    url = 'http://httpbin.org/ip'    for i in range(50):      print("{}: {}".format(i, requests.get(url).text))

統計一下運行時間:

$ time python3 get.py    (輸出略...)  python3 get.py  0.56s user 0.06s system 1% cpu 35.606 total

大概花費35s鍾,當然不同網路環境可能結果不同。而且列印出來的結果是按照1, 2, 3...50這樣的順序來的,說明整個請求是同步的,一個請求結束再發下一個請求。

使用gevent

我們再來寫一個gevent版的get.py,命名為gevent_get.py

import requests  import gevent  import gevent.monkey  # 這裡將socket變成非同步  gevent.monkey.patch_socket()    url = 'http://httpbin.org/ip'      def hello(i):      print("{}: {}".format(i, requests.get(url).text))      tasks = [gevent.spawn(hello, i) for i in range(50)]  gevent.joinall(tasks)

再來看看運行時間:

$ time python3 gevent_get.py    (輸出略...)  python3 gevent_get.py  0.49s user 0.06s system 39% cpu 1.403 total

我的天!才1.4秒左右,而且列印出來的結果沒有按照1, 2, 3...50這樣排列,而是按照隨機順序排列的,說明整個請求是非同步非阻塞的。

結論

我又多試了幾次,gevent的效率確實牛,遠遠高於同步io請求。最近在我的一個爬蟲小項目裡面用進程池 + gevent,爬取80個頁面,大概花費40s左右,每個頁面只要0.5s,而且還包括DOM解析的時間,而用同步請求則花費大概300s左右。

gevent的更多用法請參考其官方文檔。這麼簡單又好用的庫趕緊試試吧。^_^

Tags: