海量並發下充電業務優化實踐


摘要

目前在進行充電業務開發時,面對的是充電終端上報的海量並發數據。訪問緩存的TPM可達120w,訪問數據庫的TPM在3w左右,高峰時段面對的是近二十萬終端上傳的百萬條並發的實時數據。在這樣的場景下有些無傷大雅的小漏洞最終釀成了生產環境的大問題,正所謂千里之堤毀於蟻穴,面對這樣的場景,必須深入理解系統所使用的技術並對於常見問題有必備運維經驗和分析能力。本文針對這些問題及解決過程進行分析,總結過往,以饗未來。

海量並發時遇到的問題

本文中以問題為驅動,以故障分析為軸線。在分析問題的過程中總結海量並發場景中對於充電業務的優化實踐經驗。這些問題的產生原因大多並不復雜,但是正如諺語所說:會者不難,難者不會。如果沒有合適的分析手段,往往對於故障問題束手無策。希望在閱讀完本文后,可以對於以后可能發生的問題有些分析手段和分析方向有些許幫助。

首先來看下本文中所涉及的生產環境問題:

  1. 服務器CPU持續過高
  2. 訪問存儲的頻率過高
  3. 高並發時的數據亂序
  4. 其他導致實時數據處理能力下降的問題

服務器CPU過高

服務器的CPU使用率是重要的性能指標。一般的情況下,CPU在10~20%足以應對數據處理的需要,但是在生產環境中多次發生CPU因為某種原因持續超過75%並且無法自行下降的情況。CPU異常高企常常會導致數據處理能力下降的問題。針對這種情況有必要進行深入分析,查找問題原因並采取有效措施防止類似事故的發生。

首先需要排除由於上傳數據的波動引起的性能變化。如果物聯網設備離網又上線,雲端首先需要獲取一些數據進行初始化的工作,批量涌入的數據會導致服務器的性能壓力問題,但通常不需要人為干預即可恢復正常。

解決辦法:針對這個問題的分析需要強大的監控圖表的支持,使用監控圖還原故障發生時的場景並針對海量數據的突變規模和業務系統的影響程序進行分析。當然如何進行數據接口的實時監控也是個很大的問題,如果這個做不到,故障現場就無法恢復,原因分析也就無從談起。對於海量數據的監控是足以寫一篇論文的,在此不再展開,有興趣的讀者可以看下influxDB(參考資料5)和grafana的相關資料。

其次需要檢查的是相關的服務運行是否正常。如果雲端的后台是由一系列的微服務構成,而服務之間是通過同步的RPC調用相互串聯起來的,那么在涌入海量數據時由於某些服務的響應速度問題,造成實時數據處理模塊的大量線程被占用,在.NET中如果有大量線程被喚醒,同時在進行業務處理的話,也是很有可能會造成CPU的升高問題。

解決辦法:針對這個問題的分析辦法是抓取程序的dump,最好能間隔1~2分鍾連續抓取多個dump,並通過dump分析線程池中的線程堆棧,並通過堆棧查找導致線程積壓的原因(參考資料2),是調用別的服務?還是查詢數據庫?等等。找到線程大量積壓的原因,基本上就成功了大半了。

最后的辦法是檢查性能計數器。性能計數器常常被人忽略,但是其在分析CPU問題時常常能起到意想不到的巨大幫助。

案例一:某次實時數據處理程序CPU預警,排除掉前文所述的可能原因后,針對.NET CLR計數器進行分析,發現在CPU高企的階段# of Exceps Thrown/Sec這個指標也相應的升高,而該指標代表的是某個時刻的程序拋出異常的統計。后來通過統計日志,也印證了前文所述,確實在那個時刻的異常非常多。

CPU和該指標的關系:

CPU(%) 指標值(%)
45 5
16 5
73 11
82 11
66 7
32 5

MSDN的解釋如下(參考資料3):

顯示每秒引發的異常的數目。它包括.NET異常和轉換成.NET 異常的非托管異常。 例如,從非托管代碼返回的HRESULT轉換為托管代碼中的異常。
此計數器包括已處理和未經處理的異常。此計數器不是一段時間內的平均值;它顯示在最后兩個樣本(以取樣間隔持續時間來划分)中觀察到的值之間的差異。 

案例二:某次實時數據處理程序CPU預警,排除掉前文的可能原因並排除異常相關指標后,在針對.NET CLR計數器的分析過程中發現,在CPU高企的階段% Time in GC這個指標也比較高,該指標代表GC時間在整個CPU時間中的占比。在發生預警的時段內,該指標的值基本上位於30%~60%的區間內,而非預警時段該指標的值在10%左右。

MSDN的解釋如下(參考資料3):

顯示自上次垃圾回收周期后執行垃圾回收所用運行時間的百分比。此計數器通常指示垃圾回收器代表該應用程序為收集和壓縮內存而執行的工作。只在每次垃圾回收結束時更新此計數器。此計數器不是一個平均值;它的值反映了最近觀察所得值。

針對GC造成的性能問題,首先需要通過dump分析是否是由於程序本身導致了GC過於頻繁,比如不合適的字符串操作,頻繁分配大對象等等。

如果從dump中無法得知GC頻繁的原因,可以嘗試使用Server GC模式(參考資料4)。通過壓力測試發現,在使用Server GC前后,程序的處理能力相差超過一倍,例如,假設原來的時候單節點TPM200會導致CPU高企,那么在使用Server GC后,TPM達到450時CPU也不到40%,相對於以前的表現有很大改善。因此使用Server GC可以有效緩解GC過於頻繁導致的性能問題,顯著提升程序的數據處理能力。

訪問存儲媒介的頻率過高

經過統計,在充電高峰期,處理實時狀態的程序訪問緩存的TPM可以達到120w,訪問數據庫的TPM在3w左右。這個數據比其他所有模塊的總和還要高。特別是訪問緩存的頻率非常高,導致對於緩存性能的依賴非常強,在緩存性能不穩定時非常容易影響到實時數據處理模塊的性能表現。

造成該問題的原因是多方面的,總結起來有以下幾個方面的原因:

1、程序架構原因。因為終端數據上傳至雲端后,是隨機分配到不同的服務器上的程序進行處理,因此在處理數據之前必須進行上下文的恢復工作,導致訪問存儲的頻率過高。

2、物聯網通訊協議原因。受到通訊協議的限制,終端只能上傳當前時刻狀態的數據,而沒有采用重要狀態轉化的事件通知機制,無法表達數據本身所代表的業務含義。導致雲端的程序必須通過查詢終端之前的相關數據獲知數據的准確意圖。

3、業務邏輯的需要。充電終端上傳的數據是分類按照不同頻率上傳的,在處理這些實時數據的過程中,經常涉及到狀態轉化相關的邏輯處理。如果需要判斷狀態轉化則必須知道是由什么狀態轉換到當前狀態的,所以需要經常性的從緩存或數據庫中查詢相關數據。

針對以上各種原因,可以采取如下辦法:

1、優化架構設計。通過適當的方法,保證終端數據能穩定傳輸到某個運算節點。可以有效利用服務器本身的資源緩存終端的相關數據,避免每次都需要恢復上下文。

2、優化物聯網協議。減少依賴實時數據的狀態轉化判斷邏輯,而采用事件通知機制確保重點狀態的處理過程,對於實時數據則更多的用於狀態監控而不是業務邏輯判斷。

海量並發時的數據亂序

終端上傳數據是隨機分配到不同的服務器由不同的線程進行處理,因此不能保證數據的處理順序跟數據的上傳順序保持一致。因此就導致了實時數據的亂序問題,即可能新的數據被老數據覆蓋的問題。目前在生產環境中使用的實時數據處理程序是運行在Thrift上的,當其比較繁忙,特別是CPU占用較高時,經常出現數據延遲和數據亂序的問題,並因此導致了嚴重的程序邏輯錯誤。

對於該問題有兩個備選方案:

方案一:通過嚴格保證數據處理的串行化來避免該問題的發生。但是這個辦法有個最大的問題是導致了程序性能的下降,本來可以並行處理的業務被迫串行化實際上對於性能影響比較大。

方案二:優化通訊協議,在物聯網傳輸的報文都應該增加時間戳,通過檢查時間戳判斷當前數據是否有效並采取相關邏輯。該方法的最大好處是不影響業務數據的處理性能。當然該方案也有壞處,那就是在確保程序並發時還需要保證邏輯的正確性,因此需要在並發編程方面投入更多的精力。

實時數據處理能力下降

盡管CPU繁忙會導致數據處理能力下降,但是本章節所述內容與此無關。有時候雖然CPU占用率並不高,內存也很充足,但是數據處理能力莫名其妙的下降,並導致了許多業務方面的問題。

該問題的原因比較隱蔽,通過對於業務模塊的分析,發現該問題與對外推送業務有關。

正常的流程推送是:

終端數據變化->實時數據處理模塊->互聯互通模塊->第三方

而問題的出現與第三方服務接口響應較慢有關,其傳導過程如下:

第三方接口響應慢
    ->互聯互通大量線程等待HTTP返回結果
    ->互聯互通模塊線程池無可用線程
    ->實時數據處理模塊的線程同步等待互聯互通模塊的返回結果
    ->實時數據處理模塊線程池也逐步用光
    ->數據處理能力下降

當實時數據處理模塊判定狀態狀態變化時會通過互聯互通模塊進行對外推送,部分外部接口無法承接巨大的推送量而出現了訪問超時等問題。由於互聯互通模塊設計對外推送時設置的超時時間是60s,導致大量的線程在等待外部接口的返回結果。而線程池的數量時有限制的,當互聯互通模塊的線程池用光后,無法再處理外部的請求,所以請求都積壓在互聯互通模塊的數據隊列中得不到處理,而由於實時數據處理模塊當時是直接同步調用互聯互通模塊進行推送,導致其本身的大量線程也在等待互聯互通接口的返回結果,進而造成由外而內的傳導效應。當實時數據處理模塊線程池可用線程用完,也就沒法處理新來的實時數據並導致了嚴重的性能問題。

對於該問題的解決辦法也相當簡單,通過dump完全可以看到線程的堆棧,通過堆棧確定了問題原因后,推送方和接收方使用消息隊列相互解耦,最終避免了此類事故的再次發生。

總結

  • # of Exceps Thrown / Sec指標較高(>5)時,很容易導致CPU出現性能問題;
  • 在比較繁忙的業務中使用拋出異常的方式要慎重;
  • % Time in GC指標持續較高(>10)時,很容易導致CPU出現性能問題;
  • 使用Server GC有助於緩解GC導致的性能問題;
  • 在處理實時數據的場景中,數據必須包含時間戳;
  • 在處理實時數據的場景中,頻繁恢復數據的上下文會導致較高的壓力;
  • 在處理實時數據的場景中,不推薦使用無狀態的處理機制;
  • 數據推送的兩端有最好能通過MQ進行解耦,減少相互影響;

參考資料

  1. Lock and Thread Performance Counters
  2. 網站High CPU分析
  3. Performance Counters in the .NET Framework
  4. <gcServer> Element
  5. 互聯網級監控系統必備-時序數據庫之Influxdb技術
  6. 互聯網級監控系統必備-時序數據庫之Influxdb集群及踩過的坑


免責聲明!

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



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