最近迷上了gevent所以研究很多gevent相關的東西。
但是我現在不想寫相關gevent和greenlet的東西。因為這一塊內容實在太多太大太雜,我自己也還沒有完全弄明白,所以等我完全搞清楚測試也測試過了之后。我會寫一篇比較系統一點的東西來把我最近研究,和測試過的東西都展現出來。
今天先寫一個基於gevent開發的requests庫,grequests的使用。
為什么會有地方使用到grequests呢?
首先是對io密集型的需求處理。首先我們都知道,如我們去請求一個網站上的數據,正常來說我們會一條一條的跑,例如這樣。
import requests url = 'http://www.baidu.com' x = request.get(url) print x
如果是多個網站的請求 我們可能會使用一個循環以此遍歷list url對象。
但是這樣就會造一個常見的性能問題。例如中間有一個請求卡住了,或者一些情況導致一個請求長時間沒有返回,由於我們的同步請求模式,在得到返回之前我們可能會長時間處於一個io阻塞的狀態。這樣顯而易見,如果有100個請求,不管是性能還是效率肯定都是慢得沒得說的。
於是我們才會想要用一種並行的思路去解決類似的問題。如果我們同時開啟100個請求,那么最差的情況也就等最后返回的那個家伙返回就可以結束所有的請求了。性能提升是n倍,幾乎越多請求異步操作的優勢也就越明顯。但是這里就要注意了,一般我們不會同步開啟那么多請求去訪問,因為如果我們開啟那么多訪問去同時命中對方一台服務器(假設是自己的生產服務器)那么會造成非常大的壓力,所以這里我們還可以設置map()的參數將size這個決定並行數量的參數設置成你認為合理的並行值即可。
rs = (grequests.get(u, proxies=proxies) for u in urls) grequests.map(rs, size=10)
但是要注意一點,由於python里面的全局鎖的關系,並不推薦使用多線程這樣的偽並行的方式。雖然coroutines也是偽並行的方式(即線性請求,當其中一個請求遇到io等待的時候切換到另外一個coroutines繼續運行等到io返回之后再接收信息,避免長時間的等待和阻塞)所以今天介紹的grequests庫就可以在python里使用gevent基於coroutines(協程)解決這個問題。
這里演示使用代碼,異常簡單就可以使你的請求性能提升數倍。
import grequests import requests import cProfile urls = [ 'http://www.xiachufang.com/downloads/baidu_pip/2016030101.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030102.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030103.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030104.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030105.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030106.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030107.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030108.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030109.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030110.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030111.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030112.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030113.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030114.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030115.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030116.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030117.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030118.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030119.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030120.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030121.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030122.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030123.json', 'http://www.xiachufang.com/downloads/baidu_pip/2016030200.json', ] def haha(urls): rs = (grequests.get(u) for u in urls) return grequests.map(rs) cProfile.run("haha(urls)") def hehe(urls): hehe = [requests.get(i) for i in urls] return hehe cProfile.run("hehe(urls)")
下面貼出請求所用時間數據 僅供參考。
使用異步在24個請求的平均耗時測試10次計算 平均花費130毫秒
2/1 0.000 0.000 0.132 0.132 coventry.py:651(haha)
在使用同步普通的request庫請求的情況下 同樣測試10次 平均花費900毫秒 如果中途遇到有單個鏈接不穩或者超時甚至會花費1秒
2/1 0.000 0.000 1.149 1.149 coventry.py:657(hehe)
總結:
在io密集,等待io時間長的請求量級越大的情況,這樣的性能提升越是明顯,使用並發或者協程至少提升性能5倍以上我們越是應該使用異步或並行操作來減少io的等待時間。python比較有效和高效的處理方案我覺得非coroutines(協程)莫屬了,而相關庫最好用的又是gevent,所以非常值得深入研究下去,一探究竟。使用類似操作減少io等待,提升整個業務性能。
refrence:
http://stackoverflow.com/questions/16015749/in-what-way-is-grequests-asynchronous
http://johndangerthornton.blogspot.com/2015/02/grequests-examples.html
http://stackoverflow.com/questions/21978115/using-grequests-to-make-several-thousand-get-requests-to-sourceforge-get-max-r