天天看點

Python并發網絡庫 Tornado / Gevent 和 異步程式設計庫 Asyncio 及其示例

1.你用過哪些并發網絡庫?

  Tornado vs Gevent vs Asyncio

Tornado 并發網絡庫和同時也是一個web微架構

Gevent 綠色線程(greenlet)實作并發,猴子更新檔修改内置 socket

Asyncio Python3 内置的并發網絡庫,基于原生協程

2.Tornado架構

   Tornado 使用于微服務,實作 Restful 接口

底層基于 Linux 多路複用

可以通過協程或者回調實作異步程式設計

不過生态不完善,相應的異步架構比如ORM不完善

import tornado.ioloop
import tornado.web
from tornado.httpclient import AsyncHTTPClient

class APIHandler(tornado.web.RequestHandler):
    async def get(self):
        url = 'http://httpbin.org/get'
        http_client = AsyncHTTPClient()
        resp = await http_client.fetch(url)   # 異步發送請求    
        print(resp.body)
        return resp.body

def make_app():
    return tornado.web.Application([
        (r"/api", APIHandler)
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(6666)
    tornado.ioloop.IOLoop.current().start()
           

運作代碼後,在終端發送 curl http://localhost:6666/api,獲得傳回的網頁内容如下:

b'{\n "args": {}, \n "headers": {\n "Accept-Encoding": "gzip", \n "Host": "htt

pbin.org"\n }, \n "origin": "223.72.75.28, 223.72.75.28", \n "url": "https://httpbi

n.org/get"\n}\n'

2.Gevent

  高性能的并發網絡庫

基于輕量級綠色線程( greenlet )實作并發,底層是用C語言實作的

需要注意 monkey patch, gevent 修改了内置的 socket 改為非阻塞

配合 gunicorn 和 gevent 部署作為 wsgi server

#《Gevent 程式員指南》是學習Gevent 的一個比較好的資料

示例:用gevent實作并發爬蟲

import gevent.monkey
gevent.monkey.patch_all()   # 修改内置庫變成非阻塞 

import gevent
import requests

def fetch(i):
    url = 'http://httpbin.org/get'
    resp = requests.get(url)
    print(len(resp.text), i)  # 傳回結果長度,以及序号
    
def asynchronous():
    threads = []
    for i in range(1, 10):    # 添加10個線程
        threads.append(gevent.spawn(fetch, i))
    gevent.joinall(threads)
    
print('Asynchronous:')
asynchronous()
           

輸出結果:

Asynchronous:

252 5

252 1

252 9

252 2

252 7

252 4

252 6

252 3

252 8

    可以看到輸出的序号順序是不一緻的,說明使用gevent并發的發的請求,而不是順序去執行,進而大大提升了請求的效率。

3.Asyncio

  基于協程實作的内置并發網絡庫,在Py3引入進去。

Python3 引入到内置庫,協程+事件循環

生态不夠完善,沒有大規模生産環境檢驗

目前應用不夠廣泛,基于 Aiohttp 可以實作一些小的服務

示例:基于asyncio實作的異步http用戶端 aiohttp

# 基于 aiohttp 并發請求
import asyncio
from aiohttp import ClientSession

async def fetch(url, session):
    async with session.get(url) as response:
        return await response.read()
        
async def run(r=10):
    url = "http://httpbin.org/get"
    tasks = []
    
    async with ClientSession() as session:
        for i in range(r):
            task = asyncio.ensure_future(fetch(url, session))
            tasks.append(task)
        responses = await asyncio.gather(*tasks)
        for resp_body in responses:
            print(len(resp_body))
            
loop = asyncio.get_event_loop()  # 擷取事件循環
future = asyncio.ensure_future(run())
loop.run_until_complete(future)  # 在事件循環裡執行任務
           

輸出結果:

254

254

254

254

254

254

254

254

254

254

繼續閱讀