gunicorn 高性能wsgi服務器

參考https://zhuanlan.zhihu.com/p/102716258

Gunicorn是什麼

Gunicorn Green Unicorn 是一個 UNIX 下的 WSGI HTTP 服務器,它是一個 移植自 Ruby 的 Unicorn 項目的 pre-fork worker 模型。

Gunicorn啓動項目之後一定會有一個主進程Master和一個或者多個工作進程。工作進程的數量可以指定。主進程是維護服務器的運行,工作進程是實際處理請求的進程。所有請求和響應均由 Worker 處理。
同步的 Worker 一次處理一個請求。

gunicorn flask 壓測對比

測試例子: demo.py

from flask import Flask
app = Flask(__name__)


@app.route('/')
def index():
    return 'hello world!'


if __name__ == '__main__':
    app.run()

Flask啓動壓測

直接運行demo.py,使用flask自帶的WSGI

[jian@laptop practics]$ python demo.py 
 * Serving Flask app "demo" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

2.使用ab工具進行壓測

[jian@laptop practics]$ ab -n 500 -c 500 http://localhost:5000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software:        Werkzeug/0.16.0
Server Hostname:        localhost
Server Port:            5000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      500
Time taken for tests:   0.477 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      83000 bytes
HTML transferred:       6000 bytes
Requests per second:    1049.28 [#/sec] (mean)
Time per request:       476.515 [ms] (mean)
Time per request:       0.953 [ms] (mean, across all concurrent requests)
Transfer rate:          170.10 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   4.6      0      11
Processing:    13  111  29.4    122     161
Waiting:        2  110  29.5    121     159
Total:         13  114  26.1    122     164

Percentage of the requests served within a certain time (ms)
  50%    122
  66%    124
  75%    125
  80%    128
  90%    136
  95%    145
  98%    153
  99%    160
 100%    164 (longest request)

上面結果可以得出,同時併發500請求,壓測結果是這樣:

Requests per second:    1049.28 [#/sec] (mean)
Time per request:       476.515 [ms] (mean)
Time per request:       0.953 [ms] (mean, across all concurrent

Gunicorn啓動壓測

1.使用gunicorn啓動, 這裏啓動4個進程

其中: -w 爲開啓n個進程 -b 監聽地址

[jian@laptop practics]$ gunicorn -w 4 -b 0.0.0.0:8000 demo:app
[2020-01-10 21:08:56 +0800] [15563] [INFO] Starting gunicorn 19.7.1
[2020-01-10 21:08:56 +0800] [15563] [INFO] Listening at: http://0.0.0.0:8000 (15563)
[2020-01-10 21:08:56 +0800] [15563] [INFO] Using worker: sync
[2020-01-10 21:08:56 +0800] [15670] [INFO] Booting worker with pid: 15670
[2020-01-10 21:08:56 +0800] [15671] [INFO] Booting worker with pid: 15671
[2020-01-10 21:08:56 +0800] [15672] [INFO] Booting worker with pid: 15672
[2020-01-10 21:08:56 +0800] [15675] [INFO] Booting worker with pid: 15675

2.使用ab工具進行壓測

[jian@laptop practics]$ ab -n 500 -c 500 http://localhost:8000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software:        gunicorn/19.7.1
Server Hostname:        localhost
Server Port:            8000

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      500
Time taken for tests:   0.111 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      86000 bytes
HTML transferred:       6000 bytes
Requests per second:    4522.80 [#/sec] (mean)
Time per request:       110.551 [ms] (mean)
Time per request:       0.221 [ms] (mean, across all concurrent requests)
Transfer rate:          759.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    4   6.9      0      16
Processing:     5   21   5.8     22      28
Waiting:        1   21   5.9     22      28
Total:         18   26   4.7     25      41

Percentage of the requests served within a certain time (ms)
  50%     25
  66%     27
  75%     28
  80%     28
  90%     31
  95%     37
  98%     40
  99%     40
 100%     41 (longest request)

上面結果可以得出,同時併發500請求,壓測結果是這樣:

Requests per second:    4522.80 [#/sec] (mean)
Time per request:       110.551 [ms] (mean)
Time per request:       0.221 [ms] (mean, across all concurrent

可以明顯看到Requests per second: 明顯比flask自帶的要高 而且Time per request 也少了很多

問題

gunicorn併發比flask好的原因

1.單 Worker

只有一個進程在跑所有的請求,而由於實現的簡陋性,內置 webserver 很容易卡死。

並且只有一個 Worker 在跑請求。在多核 CPU 下,僅僅佔用一核。

當然,其實也可以多起幾個進程。

2.缺乏 Worker 的管理

加入負載量上來了,Gunicorn 可以調節 Worker 的數量

flask內置的 Webserver 是不適合做這種事情的

一言以蔽之,太弱,幾個請求就打滿了

gunicorn和flask通信 流程
nginx<->gunicorn<->flask

gunicorn 幾種 worker 性能測試比較

1.Gunicorn目前自帶支持幾種工作方式:

  • sync (默認值) :最簡單的同步工作模式
  • eventlet :基於Greenlet庫,利用python協程實現的
  • gevent: 基於Greenlet庫,利用python協程實現的
  • tornado: 利用python Tornado框架實現
  • gaiohttp:利用aiohttp庫實現異步I/O,支持web socket
  • gthread:線程工作模式,利用線程池管理連接

sync

sync 模式(同步工作模式)
這是最基本的工作模式,也是默認的工作模式,線程爲native類型。即請求先來後到,排隊模式。

eventlet 模式(協程異步)

eventlet 工作模式是基於eventlet庫,利用python協程實現的。
要使用該工作模式的話必須先安裝eventlet庫,並且版本要大於等於0.24.1

gevent模式(協程異步)

gevent是基於Greentlet庫,利用python協程實現的。
Gunicorn允許通過設置對應的worker類來使用這些異步Python庫。這裏的設置適用於我們想要在單核機器上運行的gevent

tornado模式

tornado利用python Tornado框架來實現。 安裝的tornado庫的版本要大於等於0.2。

gthread模式

gthread採用的是線程工作模式,利用線程池管理連接,需要安裝gthread庫。

參數

--worker-class STRTING要使用的工作模式,默認爲sync異步

-w INT,  --workers INT用於處理工作進程的數量,爲正整數,默認爲1;

2.安裝測試模塊

[jian@laptop tmp]$ cat requirements.txt 
gunicorn==19.7.1
flask==1.1.1
flask-redis==0.4.0
gevent==1.2.2
tornado==4.5.3
eventlet==0.25.1
#這裏要特別注意tornado版本必須是5.0以下,不然gunicorn 在啓動會報錯:
TypeError: __init__() got an unexpected keyword argument 'io_loop'
[jian@laptop tmp]$ pip install -r requirements.txt

測試例子

測試環境: Fedora 29 x64

需要安裝redis , 可以使用下面命令進行安裝:

[root@laptop ~]# dnf install redis

開啓redis服務:

[root@laptop ~]# /usr/bin/redis-server /etc/redis.conf

測試程序:

from flask import Flask
from flask_redis import FlaskRedis

REDIS_URL = "redis://:@localhost:6379/0"
app = Flask(__name__)
app.config.from_object(__name__)

redis = FlaskRedis(app, True)


@app.route('/')
def index():
    redis.incr("hit", 1)
    return redis.get("hit")


if __name__ == '__main__':
    app.run()

開始測試

分別使用四種方式開啓服務

[jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class sync
[jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class gevent
[jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class tornado
[jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class eventlet

使用ab工具,並行500個客戶端,發送50000次請求,壓測命令:

[jian@laptop practics]$ ab -c 500 -t 30 -r http://localhost:8000/

測試結果

Worker class Time taken for tests Complete requests Failed requests Requests per second 用戶平均請求等待時間 服務器平均處理時間 最小連接時間 平均連接時間 50%的連接時間 最大連接時間
sync 43.362s 49719 157 1146.61 436.069ms 0.872ms 12ms 55ms 25ms 33574ms
gevent 13.062s 50000 0 3827.96 130.618ms 0.261ms 3ms 129ms 96ms 1477ms
tornado 27.925s 50000 17 1790.50 279.252ms 0.559ms 16ms 146ms 27850ms 53547ms
eventlet 12.601s 50000 0 3967.88 126.012ms 0.252ms 9ms 125ms 1377ms 3123ms

eventlet 和gevent兩種方式效果最好,數據基本差不多.

gunicorn 配置

workers模式

每個worker都是一個加載python應用程序的UNIX進程 worker之間沒有共享內存
建議workers 數量是 (2*CPU) + 1

多線程模式

gunicorn 還允許每個worker擁有多個線程

在這種模式下,每個worker都會加載一次,同一個worker生成的每個線程共享相同的內存空間

使用threads模式,每一次使用threads模式,worker類就會是gthread

gunicorn -w 5 --threads=2  main:app

等同於:

gunicorn -w 5 --thread=2 --worker-class=gthread main:app

最大的併發請求就是worker * 線程 , 也就是10
建議最大併發數 是(2*CPU) +1

僞線程 gevent (協程)

gunicorn --worker-class=gevent --worker-connections=1000 -w 3 main:app

work-connections 是對gevent worker類的特殊設置

建議workers數量 仍然是 (2*CPU) + 1
在這種情況下,最大的併發請求數 是3000(3個worker * 1000連接/worker)

建議

  • IO 受限 -建議使用gevent或者asyncio
  • CPU受限 -建議增加workers數量
  • 不確定內存佔用? -建議使用gthread
  • 不知道怎麼選擇? -建議增加workers數量

其他備註

Gunicorn對靜態文件的支持不太好,所以生產環境下常用Nginx作爲反向代理服務器。

生產環境都是Nginx + gunicorn + flask

Fastapi 和 gunicorn

fastapi 自帶一個叫做 uvicorn的 wsgi 服務器,通常用於開發調試。生產環境中主要使用gunicorn作爲wsgi。

生產環境中使用gunicorn的方式如下:

gunicorn -w 1 -k uvicorn.workers.UvicornWorker -c gunicorn_conf.py main:app

fastapi要在工作進程中使用的與gunicorn兼容的工作類。fastapi中需要使用 uvicorn.workers.UvicornWorker

Uvicorn爲單進程的ASGI server,而Gunicorn是管理運行多個Uvicorn,以達到併發與並行的最好效果在fastapi中,使用gunicorn管理uvicorn,gunicorn可以啓動多個uvicorn進程,並管理這些進程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章