asyncio與gevent並發性能測試
在對網站進行掃描或者暴破時需要對網站進行高並發操作,然而
requests
+concurrent
多線程性能上不太理想,了解到python用得比較多的並發庫有asyncio
和gevent
,於是就有了如下測試。
0x00 協程
asyncio
和gevent
都是基於攜程來進行並發操作的。協程也被稱為微線程。
協程只是在單一的線程里進行切換不同的協程,因此無法使用多CPU能力,對於CPU密集型程序還是使用多進程比較好。
協程相比較進程和線程來說占用的內容更少,同樣的線程切換更多的是靠操作系統來控制,而協程的執行則由我們自己控制。
並發原理:當其中一個協程遇到io等待時,將會切換到另一個協程繼續運行。
0x01 grequests
grequests
是對requests
和gevent
庫的封裝
測試代碼:
#!/usr/bin/python3.7 import grequests import time if __name__ == '__main__': start = time.time() greenlets = [] for _ in range(10): greenlets.append(grequests.get("http://150.xx.xx.xx")) rets = grequests.map(greenlets) for ret in rets: print(ret) end = time.time() print("grequests visit_async tasks %.2f seconds" % (end - start))
grequests.map()
參數說明:def grequests.map(requests, stream=False, size=None, exception_handler=None, gtimeout=None)
參數 | 說明 | 備注 |
---|---|---|
size | 協程的並發度(相當於線程數) | 當一個協程在IO等待時,會將CPU交給其他協程 |
exception_handler | 異常處理函數 | 用於處理單個請求出現異常的函數 |
gtimeout | 設置所有請求的超時時間 |
grequests的底層是request,所以它也支持回調函數:
def print_url(r, *args, **kwargs): print(r.url) res = grequests.get(url, callback=print_url)
測試結果:

0x02 asyncio + uvloop
由於gevent
的猴子補丁的緣故,requests
可以和gevent
結合使用,但是在不清楚內部實現的情況下,requests庫經常比較容易出現Failed to establish a new connection:
的情況,在使用grequests庫之后該情況得到解決。
uvloop是用Cython寫的,目前不支持windows,它基於libuv.uvloop使得asyncio更快,基於性能的測試接近於go。
可以通過兩種方式來使用uvloop:
import uvloop import asyncio #1. 通過設置策略 asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) #2. 直接創建一個新的event_loop asyncio.set_event_loop(uvloop.new_event_loop())
由於asycnio采用異步操作,它在使用的過程中所有的模塊也都得是異步的,所以在進行http請求時也需要異步,即aiohttp
測試代碼:
#!/usr/bin/python3.7 import asyncio import aiohttp import uvloop import time async def access_url(url): async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False)) as session: async with session.get(url) as response: status_code = response.status print(status_code) async def visit_async(): start = time.time() tasks = [] for _ in range(10): tasks.append(access_url("http://150.xx.xx.xx")) await asyncio.gather(*tasks) end = time.time() print("asyncio visit_async tasks %.2f seconds" % (end - start)) if __name__ == '__main__': loop = asyncio.get_event_loop() future = asyncio.ensure_future(visit_async()) asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) loop.run_until_complete(future)
測試結果:

0x03 優缺點
asyncio
由於是異步操作,且代碼庫生態不夠完善,部分異步代碼庫存在問題可能查不到,且編寫代碼時行數較多,影響閱讀,而且代庫函數全部重構,上手有難度,但是並發執行的速度較快,對於暴破、端口掃描等比較適用。gevent
采用了requests模塊,在使用了猴子補丁后對於掃描網站路徑等可以有效即時針對掃描結果進行深層掃描。
請求內容:

參考:
- https://www.cnblogs.com/kxsph/p/9268774.html
- http://www.cnblogs.com/zhaof/p/7536569.html
- https://www.cnblogs.com/thomson-fred/p/10142226.html
- asyncio port scanner: https://gist.github.com/0xpizza/dd5e005a0efeb1edfc939d3a409e22d9