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