1. 程式人生 > >網易雲捕效能踩坑解決之道上篇

網易雲捕效能踩坑解決之道上篇

本文由作者餘寶虹授權網易雲社群釋出。


從零開始設計開發一個日處理資料8億的大資料高併發實時系統,哪些效能問題需要特別注意?這裡我們一起梳理一下,本文中我將以PE,SA同學戲稱的DDOS系統—網易雲捕設計開發實踐中兩年的時間裡碰到的真實問題,踩過的坑及解決問題的方法和大家一起討論如何解決這些問題。文中不會大談特談架構設計,只是會在提及問題出現的場景及解決方法時初略帶過,沒有場景的談架構設計都是耍流氓。本文著重列舉在雲捕業務場景下我們碰到的一系列效能問題以及解決問題的思路,幫助一部分有類似場景的人少走彎路,拋磚引玉,歡迎各路大神批評指正。
   為了便於讀者輕鬆的理解後續的描述,有必要開始之前先熟悉雲捕的業務場景,我這裡簡明扼要的介紹一下。
   有統計顯示,Crash的出現比率非常高:63%的使用者碰到過移動APP crash,在首次啟動碰到crash時,21%的使用者會解除安裝App,另外,Crash發生在使用過程中,70%的使用者會給予差評。Crash已經成為移動App開發的最大障礙,App開發中進行質量跟蹤同樣有很多難以解決的問題:使用者投訴閃退,但卻無法重現;QA投入大量時間測試,還是無法解決各類crash問題;Android機型太多太雜;產品要求快速上線,搭建一個crash收集平臺耗時耗力;使用國外的產品,網路不好,崩潰上報問題很大並且使用其它公司的產品,資料安全又難以保證。在此情況下,網易雲捕應運而生了,使命就是為了助力移動端的開發者打造高品質APP,精準捕捉APP的每一次質量問題。
   為了實現上述目的,在調研競品後我們發現雲捕需要實現以下功能:
     1 實時展示崩潰、異常、卡頓問題詳情,裝置資訊,崩潰卡頓分類;
     2 實時準確統計分析各類崩潰、異常、卡頓問題次數、影響人數,啟動人數,比率等多方位趨勢圖,實時報警讓使用者全面掌握產品質量狀況 ;
     3 實時準確統計今日問題統計,今日Top3實時展示24小時Top3問題統計排序;
     4 實時準確統計每個崩潰累計的型別裝置型號分佈圖,系統版本分佈圖;
     5 崩潰、卡頓堆疊實時自動還原; 
     6 demo實時展示;
     7 解決方案實時推薦;
     8 使用者自助接入,前期不需要稽核;
 看上面的需求不難發現,有幾個關鍵詞大資料,高併發,實時,準確。為了很好的實現這個需求便有了下面的架構設計圖:
 瞭解完業務場景,看完架構設計圖,下面的例子就比較好理解了,為了系統性,下面一個個元件的介紹問題及解決方案。

   DDB篇:


   案例一:某週六,資料量急劇增加,收到哨兵報警:Cause: java.sql.SQLException: Get null from pool;
   解決方案:首先通過命令show process或者show status檢視連結數,檢查資料庫連線數設定及釋放正常後,問了其它同事也沒有碰到這個問題, 聯絡DBA同學修改連線數後恢復正常。據DBA同學描述現在一個QS最大可支援1024個連結,可以通過增加QS的數量來提高連結;

   案例二:雲捕需要在插入資料的時候根據資料型別實時生成一個型別,某日排查問題發現插入的記錄條數和DDB節點數一致
   解決方案:仔細查閱ddb文件發現設定多節點ddb設定UNIQUE key插入不能保證唯一,只能在單節點的叢集上保證唯一性。多節點叢集需要業務方自己實現,利用redis提供的setnx來實現分散式鎖後解決問題,讀者也可以通過zookeeper來實現,請自行google;

   案例三:資料插入延遲比較高
   解決方案:1 請DBA同學升級DDB硬碟為SSD硬碟; 
        2 批量插入替代單條插入,並且批量插入要控制好batch的數字否則會出現下面的異常;
   Cause: java.sql.SQLException: condition number is: 1124, exceed max value:1024 或者Cause: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (26124567 > 16777216).
 You can change this value on the server by setting the max_allowed_packet' variable.

可以事先通過控制檯可以查到部分閾值,查不到的也可以聯絡DBA同學確認一下。

批量插入要控制batch的大小,確保引數個數和訊息體大小都在合理的範圍內

注意:資料庫的再提升效能,畢竟效能在哪裡,能不經過資料庫的就不過,能用快取扛的就用快取。往往總是策略來得更有效果,有時候優化不下去了,我們是不是想想換一種思維方式,為了開啟一個瓶子喝水,除了開啟瓶子以為,還可以把塞子推進去,換種思路有時候會海闊天空。


   多執行緒篇:


   場景:雲捕有兩個對外的介面,分別用來接收十億臺左右的裝置發上來的崩潰,卡頓,啟動資料,併發非常大。
   案例一:某日收到哨兵報警,記憶體使用率100%,上伺服器分析發現Java堆的eden區,survivor區,tenured區 全部堆滿,介面服務處於將近癱瘓的狀態,迅速dump檔案後用mat分析發現佇列裡面塞滿了物件,但是專案程式碼裡面沒有明顯的使用佇列。
   最後通過排查發現是因為使用多執行緒時excutor 內部使用了一個無界佇列,如果資料處理不夠快,大量的資料回堆積在記憶體,導致記憶體被撐爆,相關具體原始碼如下:

分析






解決辦法:使用定製的例項,設定佇列長度,及拒接策略,為了保證資料都能被完整處理,雲捕使用的拒絕策略是處理不過來時利用kafka的可堆積性重新扔回kafka中.


為了保證在重啟例項時資料不丟失,這裡還需要註冊一個鉤子到JVM實現JVM退出前先把本地佇列中的資料消費掉.

   JVM和Tomcat篇:
    
 案例:報警項:TomcatCollector,報警內容:currentThreadsBusy數: 2000,currentThreadsBusyMax數:2000
     解決方案:Tomcat預設使用BIO模式,調整為NIO模式.並調整程式碼,加解密,預處理等等統一移到資料處理模組,介面模組只負責接收資料,接收資料以後啥也不做,直接扔到Kafka叢集中,資料處理模組再從Kafka叢集中提取資料排隊處理.Tomcat三種執行模式及使用場景:

  • BIO: 同步並阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善,適用連線數較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中。。

  • NIO:同步非阻塞,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理,適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜。

  • AIO:非同步非阻塞,伺服器實現模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動執行緒進行處理適用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜。

   Kafka篇:
     案例一: 資料大幅度增加後部分使用者反饋傳送訊息超時
     
調整為如下:

      解決方案: 
kafka的QPS據說最大能達到百萬級別的QPS,但是經排查問題就出在這裡,同步改非同步,kafka支援批量傳送資料後問題解決。   
     案例二:kafka.common.FailedToSendMessageException: Failed to send messages after 3 tries.

     解決方案:這個和kafka brocker設定有關,通過分析發現一部分上報的訊息體過大,甚至達到2M,DBA專家這邊後臺也沒有查到任何異常,但是大量的訊息傳送失敗。通過反覆Google後我得到一個資訊:kafka訊息體為10K的時候效能最佳,基於這個資訊,讓DBA同學幫忙調整後臺限制batch的總大小限制,應用程式中調整批量的大小,並拋棄一部分過大的訊息體,調整以後效果如下。

    注意:另外要注意kafka的一個分割槽只能被一個節點消費,請合理的設定kafka的節點數,當節點數<kafka分割槽數,會出現部分節點無法獲取資料的情況。另外kafka分割槽過多也會導致kafka本身的可靠性降低.
  

   Redis篇:
 redis在雲捕系統的地位相當重要,碰到的問題也比較多,最近才解決了一個遺留的老大難問題.由於15年的時候才接觸到redis,使用過程中姿勢存在比較大的問題.在這裡列舉下面幾個問題:
案例一:CPU抖動,具體可以見DBA同學的文章
 redis cpu 抖動問題分析  

redis-faina redis效能問題診斷利器
案例二:主從不同步,redis記憶體無法擴容,記憶體溢位; 
 

   改造後的效果如下:




ddb連線數限制:<property name="maxActive" value="500" />         <!-- 最大連線數量 --> ddb最大支援500個連線數


org.springframework.dao.InvalidDataAccessApiUsageException: READONLY You can't write against a read only slave.; nested exception is redis.clients.jedis.exceptions.JedisDataException: READONLY You can't write against a read only slave.

+--------------------------+------------+

2 rows in set (0.01 sec)


免費領取驗證碼、內容安全、簡訊傳送、直播點播體驗包及雲伺服器等套餐

更多網易技術、產品、運營經驗分享請訪問網易雲社群


相關文章:
【推薦】 Http介面系列:如何提高Http介面用例的資料穩定性