記一次Django響應(yīng)超慢的解決過(guò)程
在本地windows機(jī)器開發(fā)的Django項(xiàng)目運(yùn)行正常,放到服務(wù)器上后響應(yīng)超慢,花了一整個(gè)工作日沒找到原因(非常絕望),又花了一整個(gè)周末才找到原因和臨時(shí)解決辦法,如果你的項(xiàng)目超慢可以參考一下解決思路。
排查過(guò)程:
1.懷疑是Python環(huán)境問題,到服務(wù)器上各種虛擬環(huán)境版本進(jìn)行嘗試,無(wú)果。
2.因?yàn)橛昧薽ysql數(shù)據(jù)庫(kù),開始用pymysql包連接改動(dòng)了一些參數(shù),擔(dān)心是驅(qū)動(dòng)問題導(dǎo)致數(shù)據(jù)庫(kù)查的慢,更換mysqlclient包后,響應(yīng)依舊慢。
3.擔(dān)心是有什么報(bào)錯(cuò)導(dǎo)致慢,于是艱難地開啟了debug模式(由于用了pymysql所以開啟debug模式也會(huì)有個(gè)報(bào)錯(cuò)),開啟之后Django反應(yīng)慢但沒有任何報(bào)錯(cuò),絕望~
4.都說(shuō)用uwsgi中間件部署Django能加快響應(yīng)速度,嘗試之,沒用。
5.作為運(yùn)維人員的思路來(lái)了-整個(gè)鏈路監(jiān)控吧,看看哪個(gè)環(huán)節(jié)慢了。在網(wǎng)上找到了django性能監(jiān)控工具django-silk,裝上之后發(fā)現(xiàn)只能看到請(qǐng)求耗時(shí)、sql查詢耗時(shí),sql查詢耗時(shí)就幾ms,也不慢啊,哭死!
6.是不是模板渲染或者代碼有問題導(dǎo)致慢呢?
views.py中新建一個(gè)方法,不做任何處理,直接返回一個(gè)字符串,依舊慢!
7.從客戶端發(fā)出請(qǐng)求到views.py處理計(jì)算這個(gè)過(guò)程很慢?
views.py的處理函數(shù)中增加print('test'),在瀏覽器中刷新網(wǎng)頁(yè)后,查看Django輸出,請(qǐng)求后要15s才能看到打印test。
8.客戶端到服務(wù)器網(wǎng)絡(luò)慢?
服務(wù)器上新建一個(gè)空白的Django項(xiàng)目,運(yùn)行在相同的端口上,反應(yīng)正常,網(wǎng)絡(luò)沒問題。
9.從Django接受到請(qǐng)求到views.py進(jìn)行邏輯處理中間這個(gè)過(guò)程很慢!中間經(jīng)過(guò)了django中間件middleware的處理,中間件導(dǎo)致的慢?
依次注釋掉能注釋的中間件,然后刷請(qǐng)求看瀏覽器發(fā)出請(qǐng)求到Django輸出test的延時(shí)。
發(fā)現(xiàn)注釋掉一個(gè)自定義的中間件后,Django很快就能輸出test(看到了希望)。但是正常業(yè)務(wù)處理方法響應(yīng)依舊慢。
10.自定義中間做了什么,怎么會(huì)耗費(fèi)這么長(zhǎng)時(shí)間?
查看中間件代碼,發(fā)現(xiàn)每次請(qǐng)求進(jìn)來(lái)Django進(jìn)來(lái)以后,都要查詢數(shù)據(jù)庫(kù),判斷當(dāng)前的url路徑是否需要進(jìn)行認(rèn)證。
但這也是一次簡(jiǎn)單的數(shù)據(jù)庫(kù)查詢而已啊,為什么會(huì)這么慢,而且前面django-silk中也顯示數(shù)據(jù)庫(kù)查詢響應(yīng)很快?
有一點(diǎn)可以肯定的是Django查數(shù)據(jù)庫(kù)這個(gè)動(dòng)作耗費(fèi)了大量的時(shí)間!
11.既然查詢數(shù)據(jù)庫(kù)這個(gè)過(guò)程慢,那抓個(gè)到數(shù)據(jù)庫(kù)的包看一下?
一頓操作后發(fā)現(xiàn),當(dāng)接收到請(qǐng)求后服務(wù)器會(huì)給數(shù)據(jù)庫(kù)發(fā)一點(diǎn)數(shù)據(jù),然后過(guò)了10多s后又發(fā)了一堆數(shù)據(jù),等這一堆數(shù)據(jù)打傳輸完后瀏覽器上網(wǎng)頁(yè)就返回了,這肯定跟響應(yīng)慢有聯(lián)系!深挖!
linux上抓包保存到文件,下載到windows上用wireshark分析發(fā)現(xiàn):當(dāng)Django收到用戶請(qǐng)求后,會(huì)主動(dòng)與數(shù)據(jù)庫(kù)主機(jī)進(jìn)行tcp連接,三次握手很快就成功了,然后等待了15s才收到MySQL的greet信息,才進(jìn)行后續(xù)的sql查詢。這說(shuō)明服務(wù)器很快就與數(shù)據(jù)庫(kù)主機(jī)建立了連接,但mysql應(yīng)用等了15s才響應(yīng)。此處不理解的,可以詳細(xì)看一下MySQL協(xié)議。
12.由于公司的DB是由DBA負(fù)責(zé)的,而且現(xiàn)在也是周末,所以暫時(shí)沒辦法繼續(xù)深挖DB原因。接下來(lái)怎么辦呢,怎么解決Django響應(yīng)慢的問題?
在服務(wù)器上繼續(xù)抓包,想對(duì)比一下主機(jī)上其他應(yīng)用查詢MySQL有什么差異,發(fā)現(xiàn)其他應(yīng)用連接MySQL時(shí)一樣會(huì)有5s的延時(shí)。在分析包的過(guò)程中發(fā)現(xiàn)別的應(yīng)用會(huì)發(fā)送ping這樣的請(qǐng)求,咦,這不是心跳包嗎?別的應(yīng)用是不是有會(huì)話保持啥的?所以沒看出來(lái)響應(yīng)慢?
13.給Django也設(shè)置一下數(shù)據(jù)庫(kù)長(zhǎng)連接會(huì)話保持試一下?
百度上關(guān)于這塊的文章都比較老了,都是通過(guò)sqlalchemy的連接池管理可以保持?jǐn)?shù)據(jù)庫(kù)的長(zhǎng)連接和復(fù)用,要改源碼操作起來(lái)比較麻煩。而且都是Django 1.4時(shí)代的解決辦法了,現(xiàn)在都Django2.2了,官方有沒有提供長(zhǎng)連接的機(jī)制支持呢?百度和查官方文檔后發(fā)現(xiàn)配置數(shù)據(jù)庫(kù)連接信息時(shí)有個(gè)可選參數(shù)叫“CONN_MAX_AGE”,默認(rèn)情況下值為0,即數(shù)據(jù)庫(kù)查詢連接用完之后就釋放掉了,新的查詢又要重新建立一次連接。
將參數(shù)設(shè)置為2個(gè)小時(shí),再次實(shí)驗(yàn)。啟動(dòng)Django后,第一次請(qǐng)求還是很慢,但后面的請(qǐng)求就加快了,問題得到了臨時(shí)解決!
Todo: 至于數(shù)據(jù)庫(kù)為什么要15s才響應(yīng)連接,這個(gè)上班后再找DBA了解具體原因。
寄語(yǔ):這次問題排查真的很艱難,嘗試了各種辦法,花了很多時(shí)間,終于通過(guò)抓包找到了相關(guān)的原因。講真,通過(guò)這次問題的排查讓我有增加了很多Django的知識(shí)!希望能對(duì)你有所幫助。
20200531更新:連接mysql慢的原因
為什么mysql響應(yīng)這么慢,百度一番后發(fā)現(xiàn)原因
mysql建立連接之前會(huì)根據(jù)連接的ip反向查找對(duì)應(yīng)的主機(jī)名,這一步會(huì)涉及DNS反向解析(如果本地hosts文件沒有指定就會(huì)找其他服務(wù)器查詢),這個(gè)過(guò)程會(huì)消耗時(shí)間。
于是登陸數(shù)據(jù)庫(kù)所在主機(jī),通過(guò)命令"nslookup IP地址"分別查詢本地IP和服務(wù)器IP,本地IP查詢結(jié)果很快返回(不在一個(gè)網(wǎng)段找不到),服務(wù)器IP結(jié)果非常慢直到超時(shí)否沒返回,這就解釋了為什么前文【windows機(jī)器反應(yīng)快,linux反應(yīng)慢】的問題。至于為什么反向解析服務(wù)器IP這么慢,這個(gè)問題就不再繼續(xù)挖下去了,應(yīng)該是網(wǎng)管沒有配置好相關(guān)的解析吧。
解決辦法:禁用反向解析,找到mysql的配置文件/etc/my.cnf,增加一行配置,重啟以后數(shù)據(jù)庫(kù)響應(yīng)速度就完美了。
[mysqld] skip-name-resolve
既然這個(gè)反向解析這么耗時(shí),為什么還要有這個(gè)流程呢?
還記得mysql的授權(quán)命令嗎:
grant all priviledges on *.* to "user"@"%" identified by "pass"
@后面的%就代表任意的主機(jī)名和ip地址,對(duì)!這個(gè)地方是可以根據(jù)主機(jī)名來(lái)授權(quán)的,如果把反向DNS解析關(guān)掉了,這里就會(huì)有問題,授權(quán)的時(shí)候就只能根據(jù)ip進(jìn)行授權(quán)啦~
到此這篇關(guān)于記一次Django響應(yīng)超慢的解決過(guò)程的文章就介紹到這了,更多相關(guān)Django響應(yīng)超慢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 在Django的View中使用asyncio的方法
- 詳解多線程Django程序耗盡數(shù)據(jù)庫(kù)連接的問題
- Django生成數(shù)據(jù)庫(kù)及添加用戶報(bào)錯(cuò)解決方案
- python自動(dòng)化測(cè)試三部曲之request+django實(shí)現(xiàn)接口測(cè)試
- 社區(qū)版pycharm創(chuàng)建django項(xiàng)目的方法(pycharm的newproject左側(cè)沒有項(xiàng)目選項(xiàng))
- Django連接本地mysql數(shù)據(jù)庫(kù)(pycharm)的步驟
- Django實(shí)現(xiàn)文章詳情頁(yè)面跳轉(zhuǎn)代碼實(shí)例
- Django如何使用asyncio協(xié)程和ThreadPoolExecutor多線程
相關(guān)文章
python?argparse的使用步驟(全網(wǎng)最全)
argparse是python的一個(gè)命令行參數(shù)解析包,在代碼需要頻繁修改參數(shù)時(shí),方便使用,主要用法就是在命令行輸入自己想要修改的參數(shù),這篇文章主要介紹了python?argparse的使用步驟(全網(wǎng)最全),需要的朋友可以參考下2023-04-04tensorflow 報(bào)錯(cuò)unitialized value的解決方法
今天小編就為大家分享一篇tensorflow 報(bào)錯(cuò)unitialized value的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-02-02Gauss-Seidel迭代算法的Python實(shí)現(xiàn)詳解
這篇文章主要介紹了Gauss-Seidel迭代算法的Python實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-06-06DataFrame窗口函數(shù)rolling()的用法
這篇文章主要介紹了DataFrame窗口函數(shù)rolling()的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02Python基礎(chǔ)之函數(shù)與控制語(yǔ)句
在調(diào)用函數(shù)的時(shí)候,如果沒有按照形參傳入指定的參數(shù),就會(huì)報(bào)錯(cuò),這時(shí),我們可以為函數(shù)的參數(shù)設(shè)置默認(rèn)的值,下面這篇文章主要給大家介紹了關(guān)于Python基礎(chǔ)之函數(shù)與控制語(yǔ)句的相關(guān)資料,需要的朋友可以參考下2022-04-04Python Socket 編程知識(shí)點(diǎn)詳細(xì)介紹
這篇文章主要介紹了Python Socket 編程,Socket又稱為套接字,它是所有網(wǎng)絡(luò)通信的基礎(chǔ)。網(wǎng)絡(luò)通信其實(shí)就是進(jìn)程間的通信,Socket主要是使用IP地址,協(xié)議,端口號(hào)來(lái)標(biāo)識(shí)一個(gè)進(jìn)程,下文詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-02-02python 使用cycle構(gòu)造無(wú)限循環(huán)迭代器
這篇文章主要介紹了python 使用cycle構(gòu)造無(wú)限循環(huán)迭代器的方法,幫助大家更好的理解和學(xué)習(xí)python,感興趣的朋友可以了解下2020-12-12