Flask中的後端併發思考(以Mysql:too many connections爲例)

以前寫過一篇《CentOS 下部署Nginx+Gunicorn+Supervisor部署Flask項目》,最近對該工程的功能進行了完善,基本的功能單元測試也作了。html

以爲也是時候進行一下壓力測試了,因此利用Jmeter對部署到服務器的項目進行了簡單的壓力測試。在以前的筆記中寫過,這個API的資源獲取,爲了避免對數據庫形成大量的讀取壓力,採用了Redis進行緩存,因此大量的GET方法下的接口都很堅挺,基本沒有出亂子,可是在其中一個須要Log數據到Mysql的接口出問題了,具體表示是數據庫插入失敗。檢查服務器上的Mysql日誌發現,錯誤內容爲:python

ERROR 1040: Too many connections

想起來以前一篇筆記中遇到Mysql server has gone away的問題,其中一步是須要對數據庫的time_out進行設置,因此天然而然,搜索這個問題的解決方案中,最初步的天然是增大mysql對於最大鏈接數的上限:mysql

show variables like 'max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+
1 row in set (0.00 sec)

而後對mysql的最大鏈接數進行修改來嘗試解決問題。具體可見參考一。但是這個方法有兩個問題:redis

1.最大鏈接數設置到多少比較合適?太大Mysql會佔用服務資源。sql

2.極端高併發狀況下,只是容許更大的鏈接,但是Mysql的I/O瓶頸仍是會形成有些服務若是等待Mysql操做完成再返回極可能好久甚至超時。數據庫

 

考慮到我這個接口中,是存儲日誌,也就是大量寫入Mysql數據並且無需校檢。因此我決定採用異步來解決這個問題:flask

1.請求過來的時候先處理請求並當即返回給客戶端windows

2.日誌寫入這個功能作成異步,也就是後臺操做,考慮到高併發一般不會太持久,把日誌寫入的壓力分散到後面的時間是比較可行的一個辦法。api

因此找到了這個異步操做的Python庫Celery, 簡單來講就是把耗時的操做丟給他去處理,Flask(或者說Gunicorn)無論這個操做而在處理完成請求以後直接返回。緩存

 

對於爲何Flask應用一步步加上了Redis, 加上了Gunicorn(Gevent),到如今須要Celery, 我畫了幾張張圖來理解。

一個典型的Flask應用(自帶調試WSGI):

可是這個的問題在於他是阻塞的,每次請求過來沒處理完沒辦法處理下一個請求!因此在調試的時候,會有提示你:

WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.

 因此真正用的時候咱們都要吧WSGI更換成Gunicorn, 利用服務器的多進程來達到併發,也就是同時處理多個請求。事實上,在利用了協成的Gevent後,每個gunicorn的worker還能夠處理多個請求:

到這一步後的問題是每一個worker都要去對mysql的資源進行處理,這就形成數據庫壓力大並且響應速度慢。因此咱們就利用Redis來緩存經常使用的數據在內存中來達到加速資源訪問的目的:

可是,今天的問題出來了,利用Redis達到了對資源緩存減輕數據庫壓力的目的,可是對Mysql的寫入呢,每一個worker 每一個請求仍是要直接寫入數據庫(好比個人api的Log),那麼這個在高併發的時候就是個問題了。因此利用Celery,而celery自己不存儲資源,他須要一箇中間人來幫忙存儲異步處理的數據,既然官方推薦也有Redis我天然就把redis做爲中間人。也就是說Celery會以隊列的形式,不斷的從中間人那裏拿到本身的任務並後臺進行處理。

也就是說每次請求,若是還有對數據庫的寫入,那麼咱們把它延遲執行而不是等它執行完畢才返回給客戶。這樣子Flask就能夠不斷的接收新請求,並且經過對於延遲執行時間的調度,咱們能夠把高峯時間的寫入請求壓力分散到後續的時間中去。

總結起來:

1.Gunicorn和gevent保證了flask能夠同時處理多個請求

2.利用Redis/Memecached 緩存能夠減輕數據庫的讀取壓力
2.可是若是請求耗時(好比大量的數據庫插入,發送驗證郵件等),你這個進程資源仍是有被卡住的可能。

3.而利用Celery來後臺處理耗時任務能夠保證Flask可以較快響應並且不被阻塞,同時減輕了數據庫的高峯寫入壓力。

 

注意:

1.Celery的worker和Gunicorn同樣須要啓動,能夠與flask服務放在一塊兒經過supervisor來管理,這樣他們能夠保持協同工做。

2.Windows的Celery只支持到3.1.25,因此你若是在windows上調試,請指定該版本。

3.若是你的後臺任務是操做數據庫,操做完成後記得釋放數據庫鏈接,例如Session.remove(Sqlalchemy scoped_session).

3.具體的操做步驟可見參考

參考:

1.https://bluemedora.com/mysql-performance-max-connections/

2.使用Jmeter進行負載測試

3.在 Flask 中使用 Celery

4.官方:Celery - Distributed Task Queue

5.supervisor 啓動 celery 及啓動中的問題

相關文章
相關標籤/搜索