背景: 隨著業(yè)務(wù)增長荷腊,API 接口 499,502 的數(shù)量也呈現(xiàn)增長的趨勢较幌。當(dāng)下使用的 nginx + gunicorn + flask 的框架結(jié)構(gòu)看起來已經(jīng)到達了瓶頸妻味。
故探索使用 nginx + uwsgi + flask 的架構(gòu)來提示性能。
名詞解釋:gunicorn/uwsgi都是wsgi協(xié)議(python web server gateway interface)的實現(xiàn)瓷式,
它們做的事情是協(xié)議轉(zhuǎn)換替饿,協(xié)議的一頭是web app(如flask, django等framework的app),
另一頭是web server(如apache, nginx等)蒿往,gunicore/uwsgi在默認的情況下都是同步模型盛垦,
但都比通常的web framework實現(xiàn)得好湿弦。
使用的版本:
gunicorn==20.0.4
uWSGI==2.0.20
測試服務(wù)器信息:
CPU&內(nèi)存
8核16 GiB
操作系統(tǒng)
Ubuntu 16.04 64位更換操作系統(tǒng)
實例規(guī)格
ecs.t5-c1m2.2xlarge(性能約束實例)升降配
實例規(guī)格族
ecs.t5
開始測試:
uwsgi 配置詳情
# http 方式
[uwsgi]
http = :5001
chdir = /home/www/xxx/xxx/
wsgi-file = scripts/web.py
callable = app
processes = 1,4,8,16
threads = 1
# socket 方式
[uwsgi]
module = scripts.web:app
master = true
processes = 1,4,8,16
socket = hebe.sock
chmod-socket = 660
vacuum = true
die-on-term = true
gunicorn 配置詳情
./ve gunicorn --preload \
--backlog 2 \
-w 1, 4, 8, 16 \
-b 10.111.1.180:8011 \
-b 127.0.0.1:8011 \
-c /home/www/xxx/xxx/scripts/gunicorn_config.py \
scripts.server:app
測試工具 wrk
`[root@jerrik /]# wrk -t12 -c100 -d30s [http://www.baidu.com ](http://www.baidu.com%20/)`
`Running 30s test @ [http://www.baidu.com](http://www.baidu.com/)`
`12 threads and 100 connections`
`Thread Stats Avg Stdev Max +/- Stdev`
`Latency 211.76ms 304.92ms 1.97s 88.17%`
`Req/Sec 72.93 68.72 797.00 90.97%`
`23725 requests in 30.05s, 347.47MB read`
`Socket errors: connect 0, read 48, write 0, timeout 50`
`Requests/sec: 789.57`
`Transfer/sec: 11.56MB`
解釋說明
- 12 threads and 100 connections:
- 總共是12個線程,100個連接(不是一個線程對應(yīng)一個連接)
- latency和Req/Sec:
- 代表單個線程的統(tǒng)計數(shù)據(jù),latency代表延遲時間,Req/Sec代表單個線程每秒完成的請求數(shù)瓤漏,他們都具有平均值, 標(biāo)準(zhǔn)偏差, 最大值, 正負一個標(biāo)準(zhǔn)差占比。一般我們來說我們主要關(guān)注平均值和最大值. 標(biāo)準(zhǔn)差如果太大說明樣本本身離散程度比較高. 有可能系統(tǒng)性能波動很大.
- 23725 requests in 30.05s, 347.47MB read
- 在30秒之內(nèi)總共有23725個請求,總共讀取347.47MB的數(shù)據(jù)
- Socket errors: connect 0, read 48, write 0, timeout 50
- 總共有48個讀錯誤,50個超時.
- Requests/sec和Transfer/sec
- 所有線程平均每秒鐘完成了789.57個請求,每秒鐘讀取11.56MB數(shù)據(jù)量
CPU 密集型測試:
@app.route('/py.cpu')
def python_cpu():
"""
測試 cpu 性能
:return:
"""
def double_fact(x):
ans = 1
for i in range(1, x + 1):
if i % 2 == x % 2:
ans *= i
return ans
def asin(x, t):
answer = 0
for k in range(0, t + 1):
a = (double_fact(2 * k - 1) / double_fact(2 * k)) * (pow(x, 2 * k + 1) / (2 * k + 1))
print("k=%d,a=%s" % (k, a))
answer += a
return answer
app.logger.info(asin(1, 100) * 2)
return success(dict(version=sys.version))
IO 密集型測試:
@app.route("/py.io")
def python_io():
"""
測試網(wǎng)絡(luò) io 性能
:return:
"""
import requests
url = "https://www.idejian.com/"
requests.get(url) # 發(fā)get請求
app.logger.info(sys.version)
return success(dict(version=sys.version))
結(jié)果比較: ./wrk -t 16 -c 100 -d 10 https://xxxx.com/py.cpu
CPU 密集型API
結(jié)果比較: ./wrk -t 16 -c 100 -d 10 https://xxxx.com/py.io
IO 密集型API
結(jié)論:同步調(diào)用下,uwsgi 并沒有比 gunicorn 更高性能蔬充。和預(yù)期不符蝶俱,不知道是否是測量的方式有問題。 暫時不考慮使用 uwsgi 饥漫,繼續(xù)優(yōu)化 gunicorn
gunicorn 優(yōu)化項目
- 調(diào)整進程數(shù)據(jù)為 cpu *2 + 1
- gunicorn worker_class = "eventlet"
- gunicorn preload 關(guān)掉
- gunicorn backlog 調(diào)整為 2048
在測試環(huán)境中測下來榨呆,上面的四項調(diào)整有明顯的性能提升,但也帶來了新的問題庸队。
- 異步的為每個連接創(chuàng)建一個“綠色”線程积蜻,在處理IO機密型程序時,有明顯的性能提升彻消。
- 弊端是為每個連接創(chuàng)建一個“綠色”線程竿拆,在高峰期的時候,mysql 等其他有連接池的工具宾尚,不能提供足夠的鏈接丙笋。導(dǎo)致程序報錯。