技術分享:高效能高併發系統的穩定性保障
作者:肖飛,於2011年8月份加入京東,曾親身參與到京東的應用效能監控、統一日誌、流式計算、記憶體快取、四層防攻擊等一些基礎技術平臺的研發和搭建工作,經歷了京東的技術系統從簡單粗放向複雜精細化的演變過程。目前主要工作為多中心交易專案中的資料複製中介軟體JingoBUS的研發。平時也會開發一些公共的平臺和工具,關注分散式系統的實現、程式設計、效能優化、開發語言等。
本文是2015年肖飛在內部分享的《高效能高併發系統的穩定性保障》PPT內容。
效能、併發、穩定性三者關係
- 高效能:高吞吐量、低延時
- 公式:吞吐量(併發)=單位時間/平均延時
- N-th% Latency:TP99, TP999
- 穩定性:低延時的穩定性標準為TP99/TP999是隱含的必要條件;系統的穩定性標準:高+可用;使用者標準
吞吐量:QPS, TPS,OPS等等,併發。並不是越高越好,需要考慮TP99。使用者角度:系統是個黑盒,複雜系統中的任何一環到會導致穩定性問題。SLA:在某種吞吐量下能提供TP99為n毫秒的服務能力。降低延時,會提高吞吐量,但是延時的考核是TP99這樣的穩定的延時。
如何改善延時
你應該知道如下表格
原文:http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html
JeffDean
Disk random read IOPS:
IOPS = 1000 / (4 + 60000/7200/2) = 122
IOPS = 1000 / (4 + 60000/10000/2) = 142
IOPS = 1000 / (4 + 60000/15000/2) = 166
SSD random read IOPS:
IOPS = 1000000/16=62500
數字的啟示
- 快取記憶體的威力;
- 執行緒切換代價cache miss
- 順序寫優於隨機寫
- 區域網絡快於本地HDD
- 大塊讀優於小塊讀
- SSD解決隨機讀寫
- 跨地域IDC網路是最大的延時
策略
- 關鍵路徑:“28原則”(20%的程式碼影響了80%的效能問題,抓重點)、“過早優化是萬惡之源”。不同解讀;
- 優化程式碼:空間換時間:各級快取;時間換空間:比如傳輸壓縮,解決網路傳輸的瓶頸;多核並行:減少鎖競爭;lesscode;各類語言、框架、庫的trick;演算法+資料結構,保持程式碼的清晰、可讀、可維護和擴充套件;
- 通過效能測試和監控找出瓶頸
metric
通過效能測試和監控:
- 單系統operf/jprofiler etc;
- Java的一系列工具:jstat, jstack, jmap, jvisualvm,HeapAnalyzer, mat
- 分散式跟蹤系統:Dapper,鷹眼等
benchmark
微觀
- 記憶體分配
吞吐量和利用率的權衡
顯式分配器:jemalloc/tcmalloc代替預設的ptmalloc
隱式分配器:JVM GC的各種調優
是否使用hugepagen預分配和重用:Netty的Pooled ByteBuf
減少拷貝:new ArrayList(int), new StringBuilder(int)
記憶體分配器利用率:減少內部或外部碎片;Page Table(頁表), TLB(頁表暫存器緩衝),減少TLB miss,pin cache。增加COW的開銷, 與記憶體分配器的實現衝突。JVM的GC調優是很多Java應用的關注重點。
- 減少系統呼叫
批處理: buffer io,pipeline
使用使用者態的等價函式: gettimeofday ->clock_gettime
減少鎖競爭
RWMutex
CAS
Thread local
最小化鎖範圍
最小化狀態,不變類
批處理增加了記憶體拷貝的開銷,但是減少了系統呼叫開銷,減少了上下文切換的影響。bufferio的例子:日誌、網路讀寫。pipeline的例子:redis。
- 減少上下文切換
觸發:中斷、系統呼叫、時間片耗盡、IO阻塞等
危害:L1/L2 Cache Missing,上下文儲存/恢復
單執行緒:基於狀態機redis和Master/Worker的nginx
CPU親和性繫結
ThreadPool的配置,不同任務型別不同的ThreadPool
幾個例子:1、docker中執行緒池大小的核數自動設定;2、CPU節能模式;3、CENTOS-7.1核心BUG。
- 網路
核心TCP Tuning引數和SocketOption:net.ipv4.tcp_*
TCP Socket連線池
網路I/O模型
傳輸壓縮
編解碼效率
超時、心跳和重試機制
網絡卡:多佇列中斷CPU繫結;增加頻寬:萬兆、Bonding;Offload特性:ethtool -k eth0;UIO Driver: DPDK
連線池:減少握手、減少服務端session建立消耗。網路I/O模型:BIO、Non-Blocking IO、AIO;select/poll、epoll/kqueue、aio;netty使用nativetransport。Offload特性:ethtool-k eth0。 將資料包分組、重組、chksum等從核心層放到硬體層做。
如何提高吞吐量
改善和降低單機的延時,一般就能提高我們的吞吐量。從叢集化上講,因素就比較多。
巨集觀
- 提升系統擴充套件能力
- 應用的無狀態架構
- 快取/儲存的叢集架構:冗餘複製(負載均衡、異構解除系統依賴);分散式(資料sharding , 副本,路由,資料一致性);切換
- 微服務/SOA
- 擴容
- 非同步化
- 快取
複製
- 通過複製提高讀吞吐量、容災、異構
- 通過資料分片,提高寫吞吐量
- 程式雙寫:一致性難以控制,邏輯複雜,冪等性要求。完全把控複製和切換時機。異構系統唯一選擇。 同步雙寫(資料一致性高,影響效能,不適合多個複製集); 非同步雙寫(資料一致性差,效能高,適合多個複製集);CDC[Change Data Capture](canal,databus等)
- 底層儲存複製機制:一致性由底層控制,對應用端透明。程式和底層儲存配合切換
擴容
- 每年大促前的核心工作:該擴容了嗎?現狀分析;擴容規劃(關鍵系統峰值20倍吞吐量);擴容依據(架構梳理、線上壓測);
- 擴容checklist:前(部署、DB授權....);後(配置更新、LB更新、接入日誌、接入監控....)
- 應用擴容、資料擴容、寫擴容、讀擴容
- 垂直擴容:加記憶體、升級SSD、更換硬體。資料複製、切換
- 水平擴容:資料遷移或初始化
現狀分析:去年雙十一到目前,峰值時的效能資料;軟硬體效能指標;資料儲存容量。
擴容規劃;流量規劃:核心系統20倍吞吐量;資料增長量規劃;擴容依據;架構梳理;線上壓測。
讀擴容比寫擴容難;讀寫分離。
非同步化
- 解耦利器
- 削峰填谷
- 頁面非同步化
- 系統非同步化
- JMQ
- 狀態機(worker)+DB
- 本地佇列
- 集中式快取佇列
本地記憶體佇列:實時價格回源服務響應之後,通過BlockingQueue非同步更新前端快取。本地日誌佇列:庫存預佔。集中式快取佇列:商品變更任務下發系統。
非同步化的一些例子:
1、作業系統核心的快取記憶體佇列,磁碟延遲刷盤;
2、mysql資料庫複製、redis複製;
非同步化需要注意的是:
1、任務要落地;
2、不可避免的重複執行,需要冪等;
3、是否需要保證順序、如何保證順序。
快取
- 久經考驗的區域性性原理
- 多級快取:瀏覽器browser cache、cdn、nginx本地redis快取、本地JVM快取、集中式快取...
- 快取前置:2/8原則、單品頁、實時價格、庫存狀態
- 一致性、延遲權衡
- 快取主節點負責寫,和最重要的校驗
- 通過CDC監聽資料庫binlog主動更新快取
- CPU不是瓶頸,網路才是
- 優化編碼,減少尺寸
- 優化操作
- 優化拓撲
如何保障穩定性
巨集觀
- 提高可用性
- 分組和隔離
- 限流
- 降級
- 監控和故障切換
可用性
- 可用性衡量指標:幾個9
- 可用性度量:A = MTBF / (MTBF + MTTR)
- 減少故障、加長可用時間
- 減少故障修復時間(發現、定位、解決)
- 冗餘複製、災備切換,高可用的不二法門
- 如何快速切換?
- 切換的影響
- 監控、ThoubleShooting、軟體質量的影響
可行性指標:999,一週10分鐘;9999,一週1分鐘不可用。可用性:從客戶角度。可用性度量:A = MTBF / (MTBF + MTTR) ,其中MTBF表示mean time betweenfailures,而MTTR表示maximum time to repair or resolve。
高可用行性的成本和收益,好鋼用在刀刃上。
如何快速切換:有可以切換的?可以不重啟應用麼? 操作快捷麼?演練過麼?
切換的影響:切換目標資源能否承受新增的壓力;切換是否影響狀態(資料的一致性、丟失問題)。
監控到位、即時,減少故障發現時間;監控全面,增加故障分析時可以參考的資料。
troubleshooting的能力,踩坑的精力, COE,問題本質、根源的追查。
軟體質量:編碼是否健壯、(異常處理、防禦性、2/8原則)超時處理、日誌是否全面合理、執行緒名稱等等。
測試:case是否全面、自動迴歸。
上線:是否灰度:N+1, N+2;回滾方案、資料回滾。
分組和隔離
- 網路流量隔離:大資料單獨部署,QOS;
- 業務系統隔離:秒殺系統獨立出主交易;
- 流量分組:對使用者按照重要程度、請求量、SLA要求等因素分級
- 儲存的分組:按照使用者重要程度、實時性要求等因素,將資料庫的複製集分組
傳統世界的例子:道路被劃分為高速道路、自行道、人行道等,各行其道。
流量分組
舉例:商品基礎資訊讀服務。對使用者按照重要程度、請求量、SLA要求等因素分級,將服務例項和儲存分組:交易、生產、網站、移動、promise、ERP...
讀寫分離
舉例:商品主資料服務。按照使用者重要程度、實時性要求等因素,將資料庫分組:ERP、POP、網站、大資料平臺...
限流
- 限流原則:影響到使用者體驗,謹慎使用
- 區分正常流量和超預期流量:限流標準來自壓力測試、折算
- 讀少限,寫多限
- 客戶端配合限流
- 不同分組的限流閾值
- 各層限流手段
前置限流,快速失敗:比如通過提供給呼叫方的JSF客戶端,封裝限流邏輯。
Nginx層限流:自主研發的模組;幾個規則:賬戶,IP,系統呼叫流程。
應用限流:減少併發數執行緒數;讀少限,寫多限;DB限流;連線數。
降級
- 保證使用者的核心需求
- 降級需要有預案和開關:確定系統和功能級別,是否可降,影響如何;降級需要有開關
- 非關鍵業務遮蔽:購物車的庫存狀態
- 業務功能模組降級:實時價格更新不及時;peking庫,保訂單管道、生產,暫停統計相關
- 資料降級:動態降級到靜態;遠端服務降級到本地快取:採銷崗服務
監控和切換
- 無所不在的監控:網路流量;作業系統指標;服務介面呼叫量、TP99、錯誤率...;日誌;業務量變化;太多監控了,如何提高監控的質量
- 切換:切換開關;成熟的流程可自動化;資料的重要性、一致性,要求強一致的,可以人工介入;系統的指標沒法判斷、監控點不全的,需人工判斷決定
review
Nginx層限流:自主研發的模組;幾個規則:賬戶,IP,系統呼叫流程。
應用限流:減少併發數執行緒數;讀少限,寫多限;DB限流;連線數。
如何驗證效能和穩定性
- 線上壓測:兩類壓力測試場景(讀業務壓測、寫業務壓測);壓力測試方案(從叢集中縮減伺服器、複製流量、模擬流量、憋單)
- 全流程演練:降級、切換等
讀業務壓力測試:是將線上業務隔離後,壓測至系統臨界點,通過分析系統在臨界點時軟硬體指標定位系統短板並優化。
寫邏輯壓力測試,如果資料具有不可恢復性,一定要提前做好資料隔離保護,如訂單號壓測,為避免影響線上業務,壓測前後都要做好“跳號”以隔離線上資料。
從叢集中縮減伺服器。加大單臺伺服器的壓力。大概估算出正常的叢集規模能夠承載的流量。
複製流量。主要通過 Tcpcopy 複製埠流量,多層翻倍放大流。
模擬流量。模擬流量主要指令碼攻擊工具和壓測工具結合,主要用ab,siege,webbench,loadruner通過多臺機器壓測。分機房,按分支進行壓測。
憋單。主要針對後續的訂單生產系統壓測。通過在管道積壓一批訂單,然後快速釋放,形成對後續生產系統持續快速的衝擊,達到壓測的目的。
效能調優學習導圖分享
需要高清思維導圖,和免費的架構師合集資料的朋友。
可以加架構交流學習群:828545509,裡面會分享一些資深架構師錄製的視訊錄影:有Spring,MyBatis,Netty原始碼分析
,高併發、高效能、分散式、微服務架構的原理,JVM效能優化這些成為架構師必備的知識體系。還能領取免費的學習資源,相信對於已經工作和遇到技術瓶頸的碼友,在這個群裡會有你需要的內容。
點選連結加入群聊【Java高階架構師學習群】:https://jq.qq.com/?_wv=1027&k=5T2kMGl