GitChat 做者:楊彪
原文:一次線上遊戲卡死的解決歷程
關注微信公衆號:GitChat 技術雜談 ,一本正經的講技術html
【不要錯過文末活動】java
故事是發生在幾個月前的線上真實案例,我將在本文中以故事形式爲你們還原此次解決遊戲卡死的經歷過程,其中有不少線上實戰經驗和技巧都值得分享借鑑的,也有做者自創的處理線上問題「四部曲」--望問聞切,還有最經典的「甩鍋」祕訣。無論白貓黑貓,能立馬解決線上問題的就是好貓,線上問題實戰經驗最重要。下來就讓我先來回顧下此次事故發生的背景吧。git
公司的遊戲得到了Google Play的最佳新遊推薦位展現,這表明着公司遊戲能夠在Google Play首頁持續一週的全球推薦。若是對Google Play還不瞭解的小夥伴們能夠看看下圖,展現了Google Play推薦位的效果:算法
就是在這樣一個重大利好消息推進下,項目研發組緊急加班加點地趕製進度和進行遊戲壓力測試(之後有機會詳細寫篇遊戲壓力測試機器人實現方案),最後內網測試環境(Testing Environment)和預生產環境(Staging Environment)一切都測試正常,隨時等待更新線上正式壞境了。數據庫
像以往同樣,遊戲發佈了停服更新公告(因爲新增長了聯盟戰和幾個大的活動,擔憂不停服更新有問題),執行完停服、備份、更新等一系列自動化流程後,服務器狀態變爲「更新完畢,白名單可進入」狀態,而後通知QA進行上線前的最後一次生產環境(Production Environment)測試。整個項目的同窗和QA同窗以白名單身份在線上生產環境測試了近半個小時,沒有任何bug和異常,咱們就信心滿滿的準備開服了。編程
遊戲對外開放後,咱們像往常同樣邊觀察邊繼續作着新的工做,忽然公司運營同窗過來講遊戲怎麼感受好卡,咱們的第一反應是你網卡了吧(由於遊戲服務器在國外,中間有一道不可逾越的qiang),我也沒太在乎仍是繼續作着別的事情,後來QA同窗也說遊戲好卡啊,我本身也登錄遊戲試了下,確實挺卡,每一次操做都要等待很久,不過到如今我仍是沒有意識到服務器卡了,只是讓運維同窗查看遊戲服的log有沒有報錯,而日誌顯示彷佛一切都正常(後面會解釋爲何日誌還正常地輸出)。緩存
慢慢地遊戲內的聊天中開始有玩家反饋此次更新後遊戲太卡,並且反饋的用戶愈來愈多,我這才意識到問題的嚴重性了,遊戲服確定哪裏操做太慢反應遲鈍了(以前可能由於遊戲公測到如今大半年還沒出現過事故,因此有點掉以輕心了)。安全
公司BOSS也過來問怎麼回事,正值Google Play推薦導量的時期,公司上下很是重視。固然我知道越是面對大問題,有經驗的人就越要冷靜,我直以爲給BOSS說:「服務器有點卡了,小問題,立刻就能弄好的,彆着急」。其實當時我內心也沒底,不知道問題在哪,不過根據本身以往經驗和實踐操做,只要按照「四步曲」流程化的執行一遍,確定能找到點線索和眉目的。服務器
更多的線上應急和技術攻關能夠參照我和朋友合著的《分佈式服務架構:原理、設計與實戰》一書中的第六章「Java服務的線上應急和技術攻關」,該章中介紹了海恩法則和墨菲定律,以及線上應急目標、原則和方法,同時提供了大量的Linux和JVM命令,也有不少平時工做中經常使用的自定義腳步文件和實際案例。微信
接下來咱們一塊兒,一步步地解決遊戲卡死的問題,文章中不少截圖只是本次文章演示說明,不是當時的現場截圖,不過我會說明清楚儘可能還原線上真實過程。
下面這張圖歸納的介紹了「望問聞切」各階段須要關心和注重的事情:
前面經過對「四部曲」的介紹,你們可能會以爲很抽象,不過它是咱們解決線上問題的指導方針、核心思想,那咱們在實際項目中又是如何「望問聞切」的呢?
首先是如何發現問題
發現問題一般經過自動化的監控和報警系統來實現,線上遊戲服搭建了一個完善、有效的日誌中心、監控和報警系統,一般咱們會對系統層面、應用層面和數據庫層面進行監控。
對系統層面的監控包括對系統的CPU利用率、系統負載、內存使用狀況、網絡I/O負載、磁盤負載、I/O 等待、交換區的使用、線程數及打開的文件句柄數等進行監控,一旦超出閾值, 就須要報警。對應用層面的監控包括對服務接口的響應時間、吞吐量、調用頻次、接口成功率及接口的波動率等進行監控。
對資源層的監控包括對數據庫、緩存和消息隊列的監控。咱們一般會對數據庫的負載、慢 SQL、鏈接數等進行監控;對緩存的鏈接數、佔用內存、吞吐量、響應時間等進行監控;以及對消息隊列的響應時間、吞吐量、負載、積壓狀況等進行監控。
其次是如何定位問題
定位問題,首先要根據經驗來分析,若是應急團隊中有人對相應的問題有經驗,並肯定可以經過某種手段進行恢復,則應該第一時間恢復,同時保留現場,而後定位問題。
在應急人員定位過程當中須要與業務負責人、技術負責人、核心技術開發人員、技術專家、
架構師、運營和運維人員一塊兒,對產生問題的緣由進行快速分析。在分析過程當中要先考慮系統最近發生的變化,須要考慮以下問題。
而後解決問題
解決問題的階段有時在應急處理中,有時在應急處理後。在理想狀況下,每一個系統會對各類嚴重狀況設計止損和降級開關,所以,在發生嚴重問題時先使用止損策略,在恢復問題後再定位和解決問題。解決問題要以定位問題爲基礎,必須清晰地定位問題產生的根本緣由,再提出解決問題的有效方案,切記在沒有明確緣由以前,不要使用各類可能的方法來嘗試修復問題,這樣可能尚未解決這個問題又引出另外一個問題。
最後消除形成的影響
在解決問題時,某個問題可能還沒被解決就已恢復,不管在哪一種狀況下都須要消除問題產生的影響。
當咱們詳細地瞭解瞭如何發現問題、定位問題、解決問題和消除形成的影響後,接下來讓咱們看下本次解決線上遊戲卡死過程當中是如何具體的應用的。
第一步,找運維看日誌
若是日誌監控系統中有報錯,謝天謝地,很好定位問題,咱們只須要根據日誌報錯的堆棧信息來解決。若是日誌監控系統中沒有任何異常信息,那麼接下來就得開始最重要的保存現場了。
第二步,保存現場並恢復服務
日誌系統中找不到任何線索的狀況下,咱們須要趕忙保存現場快照,並儘快恢復遊戲服務,以達到最大程度止損的目的。
一般JVM中保存現場快照分爲兩種:
- 保存當前運行線程快照,可使用
jstack [pid]
命令實現,一般狀況下須要保存三份不一樣時刻的線程快照,時間間隔在1-2分鐘。- 保存JVM內存堆棧快照,可使用
jmap –heap
、jmap –histo
、jmap -dump:format=b,file=xxx.hprof
等命令實現。
快速恢復服務的經常使用方法:
- 隔離出現問題的服務,使其退出線上服務,便於後續的分析處理。
- 償試快速重啓服務,第一時間恢復系統,而不是完全解決問題。
- 對服務降級處理,只使用少許的請求來重現問題,以便咱們能夠全程跟蹤觀察,由於以前可能沒太注意這個問題是如何發生的。
經過上面一系列的操做後,保存好現場環境、快照和日誌後,咱們就須要經過接下來的具體分析來定位問題了。
第三步,分析日誌定位問題
這一步是最關鍵的,也是須要有不少實戰經驗的,接下來我將一步步還原當時解決問題的具體操做步聚。
診斷服務問題,就像比醫生給病人看病同樣,須要先查看一下病人的臉色如何、摸一摸有沒有發燒、或再聽聽心臟的跳動狀況等等。一樣的道理,咱們須要先查看服務器的「當前症狀」,才能進一步對症下藥。
load average一共有三個平均值:1分鐘系統負荷、5分鐘系統負荷,15分鐘系統負荷。哪咱們應該參考哪一個值?
若是隻有1分鐘的系統負荷大於1.0,其餘兩個時間段都小於1.0,這代表只是暫時現象,問題不大。
若是15分鐘內,平均系統負荷大於1.0,代表問題持續存在,不是暫時現象。因此,你應該主要觀察"15分鐘系統負荷",將它做爲服務器正常運行的指標。
說明:咱們當時服務器負載顯示並不高,因此當時第一反應就排除了承載壓力的問題。
咱們主要關注紅框中指標,它表示當前cpu空閒狀況,而其它各指標具體含義以下:
0.7%us:用戶態進程佔用CPU時間百分比,不包含renice值爲負的任務佔用的CPU的時間。
0.0%sy:內核佔用CPU時間百分比。
0.0%ni:改變過優先級的進程佔用CPU的百分比。
99.3%id:空閒CPU時間百分比。
0.0%wa:等待I/O的CPU時間百分比。
0.0%hi:CPU硬中斷時間百分比。
0.0%si:CPU軟中斷時間百分比。
說明:咱們線上服務器爲8核16G的配置,當時只有一個cpu顯示繁忙,id(空閒時間百分比)爲50%左右,其他顯示90%多。從這裏看彷佛沒有什麼太大的問題。
既然cpu負載和使用都沒太大問題,那是什麼卡住了服務呢?直覺告訴我,多是線程死鎖或等待了什麼耗時的操做,咱們接下來就來查看線程的使用狀況。不過在查看線程使用狀況以前,咱們首先看看JVM有沒有出現內存泄漏(即OOM問題,個人書中有介紹一個實際OOM的案例),由於若是JVM大量的出現FGC也會形成用戶線程卡住服務變慢的狀況。
S0:倖存1區當前使用比例
S1:倖存2區當前使用比例
E:伊甸園區使用比例
O:老年代使用比例
M:元數據區使用比例
CCS:壓縮使用比例
YGC:年輕代垃圾回收次數
FGC:老年代垃圾回收次數
FGCT:老年代垃圾回收消耗時間
GCT:垃圾回收消耗總時間
說明:當時服務也沒有出現大量的FGC狀況,因此排除了有OOM致使的用戶線程卡死。
PID:進程的ID
USER:進程全部者
PR:進程的優先級別,越小越優先被執行
NInice:值
VIRT:進程佔用的虛擬內存
RES:進程佔用的物理內存
SHR:進程使用的共享內存
S:進程的狀態。S表示休眠,R表示正在運行,Z表示僵死狀態,N表示該進程優先值爲負數
%CPU:進程佔用CPU的使用率
%MEM:進程使用的物理內存和總內存的百分比
TIME+:該進程啓動後佔用的總的CPU時間,即佔用CPU使用時間的累加值。
COMMAND:進程啓動命令名稱
說明:經過查看線程%CPU指標,明顯能看到某個java線程執行很是佔用CPU,所以判定該線程當時出現了問題。那麼咱們接下來如何找到這個線程當時在幹嗎呢?請看如下三步聚。(圖片只是示意圖,不是當時線上截圖)
查看進程中各線程佔用cpu狀態, 選出最繁忙的線程id,使用命令top -Hp pid
把線程id轉成16進制,使用命令printf 「%x\n」{線程id}
打印當前線程運行的堆棧信息,查找線程id爲0x766B的線程堆棧信息
說明:線上經過打印繁忙線程,查看線程的執行堆棧,並無找到被卡住的業務代碼,每次都是執行成功的。當時就很是納悶,爲何一直只是這一個線程在不停地消耗着CPU,忽然一個編程的小技巧幫我找到了問題的罪魁禍首——線程中任務分配不均致使的服務響應變慢。
小技巧:爲不一樣的業務線程自定義名稱,好比打印日誌的線程爲
log_xxx
,接收消息請求的線程爲msg_xxx
,遊戲業務線程爲game_xxx
等。java中具體如何爲線程命令以下圖所示:
經過上面紅框中的代碼咱們能夠看到,線程任務的分配規則是經過用戶的uuid模上線程池的長度,這樣實現的目的是想讓同一個用戶的全部請求操做都分配到同一個線程中去完成(線程親和性),這樣的實現是爲了從用戶角度保證線程的安全性,不會出現多線程下數據的不一致性。
而問題就出如今這個uuid取模上了,咱們使用的是Twitter的分佈式自增ID算法snowflake,而它生成的全部id恰好與我設置的線程池大小64取模後爲0(具體緣由不明),致使全部用戶的全部請求所有分配到了一個線程中排隊執行了。這也是爲何在查看線程堆棧信息時感受都在正常執行,而打印的全部線程中只看到編號爲0的線程在執行,其它都空閒等待。
說明:此功能實現是在上線前兩天,運營同窗告訴說,有玩家反饋前一刻領取到的鑽石在下一刻莫名消失了,個人第一反應確定是多線程形成的,因此就臨時採起了這種線程親和方式統一解決了線程安全的問題。如今找到了問題產生的緣由,接下來看是如何解決的。
第四步,Hotfix後繼續觀察狀況
在測試環境或預生產環境修改測試後,若是問題不能再復現了,能夠根據公司的Hotfix流程進行線上bug更新,並繼續觀察。若是一切都正常後,須要消除以前可能形成的影響。
此命令經過結合Linux操做系統的ps命令和JVM自帶的jstack命令,來查找Java進程內CPU利用率最高的線程,通常適用於服務器負載較高的場景,並須要快速定位負載高的成因。
此腳本最初來自於互聯網,後來爲了讓其在不一樣的類UNIX環境下運行,因此我作了一些修改,該命令在我每一次定位負載問題時都起到了重要做用。
命令格式:
./show-busiest-java-threads -p 進程號 -c 顯示條數 ./show-busiest-java-threads -h
使用示例:
./show-busiest-java-threads -p 30054 -c 3
示例輸出:
腳本源碼見《分佈式服務架構:原理、設計與實戰》書中241頁,更多服務化治理腳本請參照書中的第六章「Java服務的線上應急和技術攻關」。
在技術方面,線上問題大體分爲如下三類。
CPU繁忙型
top
、jstack [pid]
和Btrace
等工具排查解決。內存溢出型
jmap –heap
、jmap –histo
、jmap -dump:format=b,file=xxx.hprof
等查看JVM內存狀況的。IO讀寫型
vmstat
、lsof –c -p pid
等。netstat –anp
、tcpdump -i eth0 ‘dst host 239.33.24.212’ -w raw.pcap
和wireshark工具等。以上只是簡單的介紹了下相關問題分類和經常使用命令工具,因爲篇幅有限更多內容請參照《分佈式服務架構:原理、設計與實戰》書中「線上應急和技術攻關」一章,詳細介紹了各類狀況下技術命令的使用。
制定事故的種類和級別
說明:每一個公司定義的事故種類和級別都不同,具體狀況具體分析,只要公司有了統一化的標準,當咱們遇到線上問題時纔不會顯的雜亂無章,知道事情的輕重緩急,以及如何處理和何時處理。
對待事故的態度
說明:當線上出現問題後,大多數人第一反應多是「這不關我事,我寫的東西沒問題」,面對線上問題不要怕承擔責任,反而正是咱們表現我的能力的最好時機。平時你們可能作了很是多的工做,勤勤懇懇的努力奉獻着,最後BOSS連你的名字可能都沒記住,尷尬!!可是一旦線上遇到問題,可能直接就形成很大的經濟損失,全項目組甚至全公司都在關注的時候,你敢於站出來完美的解決了該問題,收穫的成就會是至關大的。固然在這個過程當中,咱們也要學會「合理地甩鍋」,具體的「甩鍋」請聽我直播。
重磅 Chat 分享:《如何在三年內快速成長爲一名技術專家》
分享人:
方騰飛 併發編程網創始人,支付寶架構師
Chat簡介:
工做前三年是職業生涯中成長最快的幾年,在這段時間裏你會充滿激情,作事專一,也容易養成良好的習慣。
在咱們公司有些同窗在前三年中就快速成爲某一個領域的技術專家,有些同窗也可能止步不前。本場Chat和你們一塊兒探討下如何在三年內快速成長爲一名技術專家。
學習方法:
掌握良好的學習心態 掌握系統化的學習方法
知識如何內化成能力
實戰技巧:
你須要學會的編碼習慣 如何在普通項目中提升本身的能力
在業務團隊作
引用文字開發如何成長
想要免費參與本場 Chat ?很簡單,公衆號後臺回覆「技術專家」