記一次Django響應超慢的解決過程

 在本地windows機器開發的Django項目運行正常,放到服務器上後響應超慢,花了一整個工作日沒找到原因(非常絕望),又花了一整個週末才找到原因和臨時解決辦法,如果你的項目超慢可以參考一下解決思路。

 

排查過程:

1.懷疑是Python環境問題,到服務器上各種虛擬環境版本進行嘗試,無果。

 

2.因爲用了mysql數據庫,開始用pymysql包連接改動了一些參數,擔心是驅動問題導致數據庫查的慢,更換mysqlclient包後,響應依舊慢。

 

3.擔心是有什麼報錯導致慢,於是艱難地開啓了debug模式(由於用了pymysql所以開啓debug模式也會有個報錯),開啓之後Django反應慢但沒有任何報錯,絕望~

 

4.都說用uwsgi中間件部署Django能加快響應速度,嘗試之,沒用。

 

5.作爲運維人員的思路來了-整個鏈路監控吧,看看哪個環節慢了。在網上找到了django性能監控工具django-silk,裝上之後發現只能看到請求耗時、sql查詢耗時,sql查詢耗時就幾ms,也不慢啊,哭死!

 

6.是不是模板渲染或者代碼有問題導致慢呢?

views.py中新建一個方法,不做任何處理,直接返回一個字符串,依舊慢!

 

7.從客戶端發出請求到views.py處理計算這個過程很慢?

views.py的處理函數中增加print('test'),在瀏覽器中刷新網頁後,查看Django輸出,請求後要15s才能看到打印test。

 

8.客戶端到服務器網絡慢?

服務器上新建一個空白的Django項目,運行在相同的端口上,反應正常,網絡沒問題。

 

9.從Django接受到請求到views.py進行邏輯處理中間這個過程很慢!中間經過了django中間件middleware的處理,中間件導致的慢?

依次註釋掉能註釋的中間件,然後刷請求看瀏覽器發出請求到Django輸出test的延時。

發現註釋掉一個自定義的中間件後,Django很快就能輸出test(看到了希望)。但是正常業務處理方法響應依舊慢。

 

10.自定義中間做了什麼,怎麼會耗費這麼長時間?

查看中間件代碼,發現每次請求進來Django進來以後,都要查詢數據庫,判斷當前的url路徑是否需要進行認證。

但這也是一次簡單的數據庫查詢而已啊,爲什麼會這麼慢,而且前面django-silk中也顯示數據庫查詢響應很快?

有一點可以肯定的是Django查數據庫這個動作耗費了大量的時間

 

11.既然查詢數據庫這個過程慢,那抓個到數據庫的包看一下?

一頓操作後發現,當接收到請求後服務器會給數據庫發一點數據,然後過了10多s後又發了一堆數據,等這一堆數據打傳輸完後瀏覽器上網頁就返回了,這肯定跟響應慢有聯繫!深挖!

linux上抓包保存到文件,下載到windows上用wireshark分析發現:當Django收到用戶請求後,會主動與數據庫主機進行tcp連接,三次握手很快就成功了,然後等待了15s才收到MySQL的greet信息,才進行後續的sql查詢。這說明服務器很快就與數據庫主機建立了連接,但mysql應用等了15s才響應。此處不理解的,可以詳細看一下MySQL協議。

 

12.由於公司的DB是由DBA負責的,而且現在也是週末,所以暫時沒辦法繼續深挖DB原因。接下來怎麼辦呢,怎麼解決Django響應慢的問題?

在服務器上繼續抓包,想對比一下主機上其他應用查詢MySQL有什麼差異,發現其他應用連接MySQL時一樣會有5s的延時。在分析包的過程中發現別的應用會發送ping這樣的請求,咦,這不是心跳包嗎?別的應用是不是有會話保持啥的?所以沒看出來響應慢?

 

13.給Django也設置一下數據庫長連接會話保持試一下?

百度上關於這塊的文章都比較老了,都是通過sqlalchemy的連接池管理可以保持數據庫的長連接和複用,要改源碼操作起來比較麻煩。而且都是Django 1.4時代的解決辦法了,現在都Django2.2了,官方有沒有提供長連接的機制支持呢?百度和查官方文檔後發現配置數據庫連接信息時有個可選參數叫“CONN_MAX_AGE”,默認情況下值爲0,即數據庫查詢連接用完之後就釋放掉了,新的查詢又要重新建立一次連接。

將參數設置爲2個小時,再次實驗。啓動Django後,第一次請求還是很慢,但後面的請求就加快了,問題得到了臨時解決!

Todo: 至於數據庫爲什麼要15s才響應連接,這個上班後再找DBA瞭解具體原因。

 

寄語:這次問題排查真的很艱難,嘗試了各種辦法,花了很多時間,終於通過抓包找到了相關的原因。講真,通過這次問題的排查讓我有增加了很多Django的知識!希望能對你有所幫助。

--------------------------------------------

20200531更新:連接mysql慢的原因

爲什麼mysql響應這麼慢,百度一番後發現原因

mysql建立連接之前會根據連接的ip反向查找對應的主機名,這一步會涉及DNS反向解析(如果本地hosts文件沒有指定就會找其他服務器查詢),這個過程會消耗時間。

於是登陸數據庫所在主機,通過命令"nslookup IP地址"分別查詢本地IP和服務器IP,本地IP查詢結果很快返回(不在一個網段找不到),服務器IP結果非常慢直到超時否沒返回,這就解釋了爲什麼前文【windows機器反應快,linux反應慢】的問題。至於爲什麼反向解析服務器IP這麼慢,這個問題就不再繼續挖下去了,應該是網管沒有配置好相關的解析吧。

 

解決辦法:禁用反向解析,找到mysql的配置文件/etc/my.cnf,增加一行配置,重啓以後數據庫響應速度就完美了。

1

2

[mysqld]

skip-name-resolve

 

既然這個反向解析這麼耗時,爲什麼還要有這個流程呢?

還記得mysql的授權命令嗎:

1

grant all priviledges on *.* to "user"@"%" identified by "pass"

@後面的%就代表任意的主機名和ip地址,對!這個地方是可以根據主機名來授權的,如果把反向DNS解析關掉了,這裏就會有問題,授權的時候就只能根據ip進行授權啦~

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