Flask中的后端並發思考(以Mysql:too many connections為例)


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

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

ERROR 1040: Too many connections

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

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

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

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

2.極端高並發情況下,只是允許更大的連接,可是Mysql的I/O瓶頸還是會造成有些服務如果等待Mysql操作完成再返回很可能很久甚至超時。

 

考慮到我這個接口中,是存儲日志,也就是大量寫入Mysql數據而且無需校檢。所以我決定采用異步來解決這個問題:

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

2.日志寫入這個功能做成異步,也就是后台操作,考慮到高並發通常不會太持久,把日志寫入的壓力分散到后面的時間是比較可行的一個辦法。

所以找到了這個異步操作的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 及啟動中的問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM