開篇、追本溯源
隨著企業產品業務不斷擴大、使用者量增加、功能需求複雜化,原有的系統架構逐漸無法滿足高效執行、快速響應市場變化以及支援大規模併發訪問等需求,在這種背景下,服務從單體應用架構,發展到資源隔離拆分多服務架構、負債均衡多叢集架構,再到更細粒度的微服務容器編排架構,業務的增長不斷促進架構的演進。本人有幸在剛進入網際網路公司沒幾年就接觸到相對大型的網際網路產品的開發,從幾十萬、幾百萬到現在上千萬 DAU,業務的增長不僅僅是對現有架構的挑戰,更是推動技術創新和架構升級的動力。企業需要不斷審視和調整其技術架構,以適應業務發展,保持競爭力,服務的部署架構以及開發者的技術認知,也跟隨著高 QPS 場景不斷的迭代升級。
這裏將會對一些高效能的技術方案進行總結,供大家品鑑,希望能給各位看官帶來一點收穫。
第一章、神兵利器:快取
想要提高服務的效能,首當其衝大家都會想到神兵利器:快取,快取可以是:應用服務的快取、獨立的快取服務。
應用服務的快取一般就是儲存一些使用頻繁、但是儲存空間佔用不大,且基本不怎麼變更的資料,或者作為多級快取中的一級。
在分散式的場景在,應用服務快取需要注意多節點快取更新問題、一致性問題、LRU 快取淘汰策略。
要解決分散式場景下的快取問題,可以使用獨立的快取中介軟體,快取不再儲存在應用服務記憶體上,直接儲存在快取的中間中,這樣多節點讀取到的資料都是同一份,就沒有上面的問題,並且快取中介軟體提供的更多的功能、和高階特性的支援,可以應對更加複雜的場景。
這裏主要對講述 Redis 的一些應用場景,以及使用過程需要注意的地方。
1.1 Redis 快取
使用Redis快取,常規的操作就是將原或者異構後的資料儲存在快取中,業務優先從快取中拿資料,拿不到再從DB同步快取,減少DB的查詢。Redis的五種數據結構,以及其他的高階功能:bitmap、pub/sub、pipeline、lua script、Transactions,分別都有各自合適的應用場景,可以透過操作命令去學習瞭解,Redis的高可用部署架構、底層的數據結構原理、版本功能特性差異,以及一些面試八股文需要掌握的內容,大家有興趣的可以谷歌搜尋下,有很多相關的學習資料,這裏就不細說。
1.2 應用場景
具體業務中需要使用哪個數據結構或者高階功能,這個需要根據場景、數據結構、效能,等多方面去評估考量和最終選擇。
這裏例舉自己在實踐過程中對Redis使用場景列舉:
1.3 效果對比
舉慄一個使用redis提升效能的 Case,高併發業務介面實時的從 DB 中查詢運營推薦的商品資料給到客戶端展示,而且運營商品資料變更不頻繁。
資料庫叢集能夠支撐的 QPS 有限,在高峰期或者大促場景下容易把資料庫打崩。這裏透過引入 Redis 中介軟體降低資料庫壓力,提升介面效能。
使用 Redis 最佳化前後對比:
修煉心得:
提升效能: 由於 redis 儲存在記憶體中,讀寫速度極快,減少資料庫訪問,從而提升了服務的效能、吞吐量,對於高併發和延遲敏感的場景尤其有效。
減輕DB壓力: 請求優先走到 redis,降低了DB的訪問,減輕了負載壓力。
靈活數據結構:redis 支援的5種數據結構和多種高階功能,靈活搭配,可以解決目前業務中大部分場景需要。
高可用: redis 有多種部署架構方式,根據不同的業務架構需要選擇,redis 的高效能設計以及分散式架構保障服務的高可用,透過資源監控預警,及時發現潛在的問題和異常,確保服務的穩定。
1.4 避坑指南
在使用快取的過程中,需要考慮到一些相對極致的場景問題,比如:快取的穿透、擊穿、雪崩、等問題。
快取穿透: 是指查詢一個一定不存在的資料時,由於快取中沒有此資料,每次查詢都會落到資料庫層。惡意攻擊或程式bug可能會不斷嘗試不存在的資料查詢,使得資料庫承受巨大壓力。
快取擊穿: 特指對於那些熱點key,在快取失效的瞬間,大量併發請求同時到達,這些請求全部穿透快取直達資料庫,對資料庫形成巨大壓力。
快取雪崩: 是指在一段時間內,大量快取在同一時刻集體失效,後續請求均直接打到資料庫,可能導致資料庫瞬間壓力過大,服務不可用。
大Key: 避免在redis中儲存大量資料,導致記憶體大量使用,以及大key操作導致堵塞,分散式部署下節點記憶體使用不均,等問題。
Key命名: key長度不宜太長,儘可能簡短且清晰表達用途,key需要能夠根據業務領域、模組區分字首,易於後續治理區分業務,尤其是對於redis叢集共用的業務,後續做大key清理、redis拆分遷移。
過期時間: 一般使用redis做熱資料儲存,也存在一些做持久化的場景,但是大部分都是需要對key設定過期時間,避免冷快取,長期佔用記憶體空間,資源被浪費。
資料一致性: 資料DB更新,未能及時同步redis,導致資料不一致問題,對於一致性要求高的業務,需要重點考慮更新策略,保障一致性。
1.5 特殊巧用
批次操作: 需要高效執行批次操作,減少網路延遲,以及提高客戶端與Redis伺服器間互動效率的場景,可以使用 pipeline 進行批次執行,提高處理速度。
多命令 原子操作 : 對於有多個命令的操作,又需要保障原子性,可以考慮使用 lua 多命令原子操作,保障資料的一致性。透過lua指令碼還可以實現各種個性化的需求,但是使用過程需要考慮效能問題、避免有過多耗時操作,堵塞了整個 redis 命令通道。
第二章、鬼斧神工:MQ
在過往的業務開發過程中,經常會有一些功能需求,它需要在覈心介面中拓展,但是非介面主要流程內容,同時可以接受一定延遲處理,我們往往會引入鬼斧神工:MQ中介軟體,巧妙的使用,將達到奇效。
2.1 應用場景
一般這些場景會用到MQ,如:高併發場景的削峰填谷、系統解耦、資料異構的業務場景、跨程序、服務的通訊、pus/sub、定時任務、等場景,需要有訊息中間 MQ:kafka、rabitmq、rockmq、... 等,透過 MQ 中介軟體的使用,保障服務的穩定、提高效能和吞吐率。
MQ 的使用場景列舉:
2.2 效果對比
舉慄一個使用釋出訂閱的簡單 Case,使用者註冊成功後,給使用者發放優惠券。
此流程中發放優惠券並非註冊的主要流程,且能夠接受註冊成功後短暫延遲收到優惠券,基於這些前提條件下,可以將這個流程單獨提取出來,使用 MQ 非同步消費實現。
使用者中心作為生產者,將註冊成功後的使用者資訊組裝上報到 MQ Topic,然後再起任務作為消費者監聽 Topi c進行非同步的消費。
後續如果有其他類似的需求,比如:註冊成功後傳送簡訊、推送訊息、等,可以複用相同 Topic,實現業務解耦。
這裏透過引入 MQ 對效能進行最佳化、業務解耦、提升註冊流程的介面效能。
使用 MQ 最佳化前後對比:
修煉心得:
業務 解耦:生產者和消費者不需要直接知道對方的存在,降低了系統的耦合度。
削峰填谷:幫助平滑系統負載,處理請求峰值,防止系統過載。
非同步處理:生產者無需等待訊息處理完成即可繼續工作,提高了系統響應速度和吞吐量。
可擴充套件性和靈活性:容易新增或更改消費者,適應系統變化。
2.3 避坑指南
ACK 機制: 訊息的執行需要確認被消費者成功消費後,才能從訊息佇列中移除。
容錯機制: 需要保障消費結果最終成功,透過異常兜底,或者訊息的死信佇列,保障異常情況下訊息也能夠兜底處理。
結果冪等: 不能完全信任生產者的訊息不重複,所以需要保障多次執行相同訊息後結果冪等。
執行時序/順序: 當存在多個消費者同時消費的時候,需要考慮消費時序問題,可能後進的會被先執行,需要保障邏輯不被影響,如果存在影響,就需要考慮將訊息進行分片路由到不同佇列中,進行多消費者消費,提高吞吐的同時,也能保障相同標識的訊息執行時序是正確的。
第三章、分身之術:非同步並行
在面對一些效能問題的時候,往往都是有一些耗時的操作(如I/O操作、網路請求、資料庫訪問等),除了將耗時的操作本身進行最佳化以外,我們還可以使用非同步程式設計,這裏使用分身之術:多執行緒、協程、等非同步程式設計。
3.1 應用場景
3.1.1 非同步並行
透過將多個 I/O 操作並行起來執行,等所有非同步操作回來後,再進行後面的邏輯,減少了 I/O 在序列同步執行流程中步步等待,將部分上下文無直接關聯的 I/O 操作並行執行,進而使整體的耗時縮短。
3.1.2 非同步執行
將一些非核心流程的耗時的操作,透過非同步程式設計,將其透過非同步的方式執行,不阻塞主流程的執行。
3.2 效果對比
舉慄一個業務場景,在業務邏輯處理之前,依賴三次的DB查詢,且這三次的DB操作互相之間沒有上下文依賴關係。
這裏使用非同步並行查詢DB的方式,對效能進行最佳化。
使用非同步併發執行前後的時序圖對比:
3.3 避坑指南
負載評估: 需要對並行操作的發起方和目標方伺服器進行負載、伺服器資源利用率的情況進行評估,在一些高併發、高峰期場景下,伺服器本身負載就高,再對其並行改造,可能會事倍功半,可能會發展成壓死駱駝的最後一根稻草,把服務搞崩潰了。
第四章、未卜先知:容量評估
想要保障服務的高效能,在設計階段或者後續的治理階段,都需要有未卜先知的能力。
根據應用的DAU、MAU,模組介面請求QPS、業務未來拓展方向、等資訊,進行容量評估,作為設計的前置資訊,衡量技術方案可行性。
容量評估包含這些內容:
QPS : 評估每秒請求數量,平均值、最高值,用於方案設計,以及效能壓測。
DAU: 評估功能日訪問量。
資料量: 評估使用者大概日生產資料量,用於使用者表設計,如:是否縱向拆分列、橫向分表、索引設計。
資源利用: 評估資料儲存佔用多少空間、應用服務佔用多少資源(cpu、mem、i/o、load、...),用於評估部署機器配置、是否擴容 。
預測增長: 評估未來增長情況,用於方案設計考慮未來可拓展、可伸縮。
第五章、洞察:效能分析
5.1 程式的效能分析工具
不同程式語言有各自專門的或通用的效能分析工具,可以幫助開發者識別和最佳化程式碼中的效能瓶頸,提高程式執行效率。
比如 golang 程式的效能分析工具。
Pprof: 透過工具採集樣本,然後透過效能剖析報告,分析效能瓶頸,常用於 CPU、記憶體、阻塞、goroutine 等多種型別的分析。
Benchmarking: 用於評估程式碼片段的執行速度,可以直接在程式碼中編寫效能測試用例,也可以匯出。
Trace: 主要用於記錄和分析單個 Go 程式的內部執行時事件,如 goroutine 的生命週期、排程、同步操作(如鎖的爭用)、系統呼叫等。Go Trace 主要關注的是程式的內部行為和執行流程,幫助開發者理解程式的執行效率和資源消耗。
5.2 分散式的效能分析
企業在微服務架構的環境下,服務依賴多基礎設施、除了儲存系統,還有各種中介軟體、以及多個內部服務呼叫,從請求發起到最終響應,整個呼叫鏈路錯綜複雜,需要對分散式服務進行鏈路追蹤,這樣才能定位到在鏈路中的哪個環節出現了問題。
分散式 追蹤 用於跟蹤一個請求從進入系統到響應返回的完整路徑,包括它所經過的所有服務、服務間的呼叫關係、呼叫儲存、中介軟體、每個環節的耗時等。這對於診斷延遲問題、監控服務依賴關係、最佳化系統性能至關重要。
比如 golang 的分散式鏈路追蹤 Jeager trace 需要一定的程式碼入侵,或者使用無程式碼入侵的 DeepFlow,基於 eBPF/cBPF,自動採集應用、網路、系統全棧效能指標。
第六章、千錘百煉:效能壓測
開發人員透過編碼實現了業務需求,功能性測試也透過了,但是並不代表它就沒問題。要保障介面的高效能,QA還需要對介面進行效能壓測,基於容量評估和效能測試的要求,衡量是否能夠達到上線的標準。
6.1 壓測標準
壓測需要制定一套標準,如:什麼介面需要效能測試?效能測試需要關注哪些指標?以及指標的基線要求。
以下是梳理我們 QA 的一些壓測標準,僅供參考
是否 壓測 : P0、P1核心業務介面、高 QPS 介面、無依賴第三方 / 內部耗時服務(演算法/搜尋..) 介面 。
壓測 配置: 最低48執行緒數,支援按倍數增長、壓測時間、壓測倍數、等。
關注指標:請求數、QPS、TPS、CPU、MEM、I/O、Load、響應時間、錯誤數、錯誤率。
判斷依據: 效能測試基線 + 容量評估。
壓測 透過基線:
響應時間 < 100ms (按以往介面估值)
TPS > 1300(post 介面按以往介面估值 tps ≈ 1000左右)
成功率 100%;
在達到介面處理能力預期指標值時,資料庫無慢查詢出現;
平均 CPU < 75%,平均負載小於 CPU 的核數;
內網流量 incoming 和 outgoing 均小於 800 Mbps;
趨勢上在併發數增長情況下,TPS 跟隨增長,響應時間 < 100ms。
壓測 報告:
壓測結束後會輸出壓測報告,透過報告分析判斷是否達到要求。
6.2 壓測工具
推薦壓測工具 Jmeter 一個由Apache組織開發的基於Java的壓力測試工具,能夠模擬大量使用者併發訪問,提供豐富的圖形界面和報告功能。
企業內部是基於 Jmeter 開發的壓測平臺,透過web頁面訪問和操作介面壓測,檢視壓測結果報表。
第七章、未雨綢繆:監控預警
高效能的系統通常需要實時、準確的效能監控。透過監控關鍵效能指標(如CPU使用率、記憶體佔用、磁碟I/O、網路頻寬、響應時間等),可以及時發現系統瓶頸和異常狀況。這些資料是衡量系統是否執行在高效能狀態的重要依據,也是調優的出發點。
監控系統不僅要收集資料,還需要具備預警功能。當監控到的效能指標超過預設閾值時,預警系統會自動觸發警告,透過郵件、簡訊、電話或整合的訊息系統通知運維人員。這樣可以在問題影響使用者體驗或造成系統故障之前,及時採取行動進行干預,保障系統的高效能執行。
在可觀測性的內容中,可以抽象出三大元素:日誌(Logs) 、跟蹤(Traces) 、指標(Metrics) ,這三大元素就是可觀測性的三大支柱。
第八章、開天闢地:架構演進
8.1 架構演進
隨著業務的迭代運營,DAU、MAU 增長,QPS 增多,爲了應對大流量帶來的三高挑戰:高可用、高併發、高效能,爲了保障使用者體驗,爲了保障服務穩定性、魯棒性,爲了支撐未來3-5年的增長,基於以上的訴求點,服務架構也隨之升級,從單體架構、拆分隔離部署、負載均衡多服務節點部署、再到更細粒度的分散式微服務部署。
架構設計與演進並不是一蹴而就的過程,不追求過度設計,遵循架構設計三大原則:簡單優於複雜、演進優於一步到位,合適優於業界領先。
暫時無法在飛書文件外展示此內容
透過服務部署保障高效能的中心思想主要圍繞以下幾個方面:負載均衡、資源隔離、冗餘備份、過載保護(限流、熔斷、降級)、彈性擴容。
暫時無法在飛書文件外展示此內容
8.2 架構圖
作為服務端開發人員,需要對應用服務的部署架構足夠熟悉,尤其是分散式的微服務部署,整體鏈路關係網錯綜複雜,當線上出現請求idletimeout網路問題、服務不可用的級聯效應、雪崩效應、 等問題的時候,如果你對部署架構沒有足夠的瞭解,就很難排查出問題,包括在做分散式系統設計的時候,也需要對當前架構有足夠熟悉。
這裏有個建議,服務端可以去找運維瞭解部署架構、部署細節,然後自己去畫架構圖,透過作圖的過程加強架構理解,同時圖可以可以更好的表達和呈現架構層級關係、流量走向、基礎設施、元件結構細節,架構圖還可以儲存歸檔,分享新童鞋,幫助其他人瞭解和自己回憶。
下面是本人在基於對企業服務部署架構的瞭解,以及在問題排查過程,逐步瞭解部署細節後繪製的架構圖。
8.2.1 分層架構圖
將部署服務架構分解為多個邏輯層次,以組織和分離系統的不同功能元件。
8.2.2 流量走向圖
請求南北流量走向
8.2.3 微服務架構圖
k8s 容器編排 + istio 服務網格
8.2.4 架構細節圖
列舉幾個架構元件的細節圖,透過圖更直觀瞭解架構元件的細節,幫助分散式方案設計、問題排查定位。
各元件 idletimeout 配置情況