从Python协程到高性能服务器
1.如何用携程实现多任务?
1.1 通过yield
import time
def task01():
while True:
print("---1---")
time.sleep(0.2)
yield
def task02():
while True:
print("---2---")
time.sleep(0.2)
yield
def main():
t1 = task01()
t2 = task02()
while True:
# next(t1)
# next(t2)
t1.__next__()
t2.__next__()
if __name__ == "__main__":
main()
1.2 通过greenlet
# 下载greenlet
pip3 install greenlet
# coding=utf-8
from greenlet import greenlet
import time
def task01():
while True:
print("---1---")
time.sleep(0.2)
gr2.switch()
def task02():
while True:
print("---2---")
time.sleep(0.2)
gr1.switch()
gr1 = greenlet(task01)
gr2 = greenlet(task02)
gr1.switch()
1.3 通过gevent
# 下载gevnet
pip3 install gevent
import gevent
def task01(n):
for i in range(n):
print("task01", i)
gevent.sleep(0.1)
def task02(n):
for i in range(n):
print("task02", i)
gevent.sleep(0.1)
def task03(n):
for i in range(n):
print("task03", i)
gevent.sleep(0.1)
print("---1---")
g1 = gevent.spawn(task01, 5)
print("---2---")
g2 = gevent.spawn(task02, 5)
print("---3---")
g3 = gevent.spawn(task03, 5)
print("---4---")
g1.join()
g2.join()
g3.join()
问题:只有gevnet中的io才能被gevent利用,我们代码中所有的io都不是gevent中的,这样我们写好的代码怎么办?答案是打补丁 gevent.monkey.patch_all()
1.4 gevent最终方案
import gevent
from gevent import monkey
import time
monkey.patch_all() # 自动将time.sleep() 替换未 gevent.sleep()
def task01(n):
for i in range(n):
print("task01", i)
time.sleep(0.1)
# gevent.sleep(0.1)
def task02(n):
for i in range(n):
print("task02", i)
time.sleep(0.1)
# gevent.sleep(0.1)
def task03(n):
for i in range(n):
print("task03", i)
time.sleep(0.1)
# gevent.sleep(0.1)
if __name__ == "__main__":
gevent.joinall([
gevent.spawn(task01, 5),
gevent.spawn(task02, 5),
gevent.spawn(task03, 5)
])
2.协程的web服务器
我们传统的框架比如flask,在高并发初使用的是线程或者进程,但这两个方案都在python的高并发中不适用,多进程(非常多)不实用的原因是没有那么多资源,多线程不能使用的原因是GIL,所以python高并发服务器的最佳解决方案是 多进程 + 协程的方式。
fastapi框架部署的时候使用的Gunicorn和uvicorn就是 多进程 + 协程 的方式。
在gevnet模块的pywsgi中有一个封装好的协程web服务器 WSGIServer,开箱即用,是遵循wsgi的协程服务器。
#!/usr/bin/python
"""WSGI server example"""
from __future__ import print_function
from gevent.pywsgi import WSGIServer
def application(env, start_response):
if env['PATH_INFO'] == '/':
start_response('200 OK', [('Content-Type', 'text/html')])
return [b"<b>hello world</b>"]
start_response('404 Not Found', [('Content-Type', 'text/html')])
return [b'<h1>Not Found</h1>']
if __name__ == '__main__':
print('Serving on 8000...')
WSGIServer(('127.0.0.1', 8000), application).serve_forever()
3.uvicorn+fastapi和gevent.pywsgi.WSGIserver的性能对比
PS C:\Users\NINGMEI\Downloads\httpd-2.4.52-o111m-x86-vc15\Apache24\bin> .\ab.exe -c 100 -n 1000 http://127.0.0.1:8001/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Server Software: uvicorn
Server Hostname: 127.0.0.1
Server Port: 8001
Document Path: /
Document Length: 13 bytes
Concurrency Level: 100
Time taken for tests: 0.740 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 138000 bytes
HTML transferred: 13000 bytes
Requests per second: 1351.95 [#/sec] (mean)
Time per request: 73.968 [ms] (mean)
Time per request: 0.740 [ms] (mean, across all concurrent requests)
Transfer rate: 182.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 12 71 13.3 69 106
Waiting: 0 55 14.2 55 91
Total: 12 71 13.3 69 106
Percentage of the requests served within a certain time (ms)
50% 69
66% 75
75% 77
80% 82
90% 94
95% 94
98% 94
99% 95
100% 106 (longest request)
PS C:\Users\NINGMEI\Downloads\httpd-2.4.52-o111m-x86-vc15\Apache24\bin> .\ab.exe -c 100 -n 1000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Server Software:
Server Hostname: 127.0.0.1
Server Port: 8000
Document Path: /
Document Length: 18 bytes
Concurrency Level: 100
Time taken for tests: 0.643 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 138000 bytes
HTML transferred: 18000 bytes
Requests per second: 1556.17 [#/sec] (mean)
Time per request: 64.260 [ms] (mean)
Time per request: 0.643 [ms] (mean, across all concurrent requests)
Transfer rate: 209.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 1
Processing: 12 61 8.5 61 84
Waiting: 0 61 8.6 61 84
Total: 12 61 8.5 61 84
Percentage of the requests served within a certain time (ms)
50% 61
66% 62
75% 64
80% 65
90% 71
95% 76
98% 79
99% 83
100% 84 (longest request)
跑在8001端口的服务是fastapi+uvicorn的helloworld 跑在8000端口的服务是WSGIServer 可以看出 WSGIServer 还是比 单uvicorn好一些,但这两种方案已经比原来的纯django或者flask的性能高很多了
个人认为如果用wsgi部署项目可以使用 WSGIServer 如果用asgi部署项目可以用 Gunicorn + uvicorn
本人仅个人观点,如有错误,请指出,共同进步。谢谢!