【億級數據專題】「消息引擎」探索服務的低延遲可用性的方案實現

2024年2月6日 25点热度 0人点赞

疾風吹征帆,倏爾向空沒。幹裡在俄頃,三江坐超忽。一孟浩然

# 背景介紹

在充滿挑戰的2023年度,我們不可避免地面對了一系列棘手的問題,例如響應速度緩慢、系統陷入雪崩狀態、用戶遭受不佳的體驗以及交易量的下滑。這些問題的出現,嚴重影響了我們的業務運行和用戶滿意度,為了應對這些問題,我們所在團隊進行了大量的研究和實踐,提出了低延遲高可用的解決方案,並在分佈式存儲領域廣泛應用。 # 專題簡介

秉持著解決問題和攻克難題的精神,我決定開展一個【億級數據專題】流量的系列探索和技術分享活動。我們希望通過這個專題系列,匯集各界的分享者和專傢,共同探討如何應對億級數據所帶來的難題和問題。

通過對有限資源的規劃,團隊還提出了分級的容量保障策略,通過限流、降級和熔斷技術等手段,保證了重點業務的高吞吐,同時,在一些對可靠性和可用性要求極高的場景下,團隊還專門推出了基於多副本機制的高可用解決方案,能夠動態識別機器宕機、機房斷網等災難場景,並實現主備切換,提高了消息存儲的可靠性和整個集群的高可用性。 ## 本文方向

本文主要面向通過對一個消息引擎發展歷程的回顧,介紹了在高峰期所面臨的低延遲挑戰,並詳細探討了低延遲高可用解決方案和基於多副本機制的高可用解決方案的特點和優勢。這些創新的舉措為保證系統的穩定運行和可靠性提供了有效的支持。

消息中間件發展史

經過多代的演進,中間件消息引擎在發展至今已經取得了巨大的進步。

- 第一階段:采用了推模式,並且使用關系型數據庫進行數據存儲。在這種模式下,消息具有低延遲的特性,在高頻交易場景中尤其廣泛應用。

  • 第二階段:采用了拉模式 推模式的雙模式消息存儲系統,例如,Kafka的吞吐性能。然而考慮到應用場景,特別是對交易鏈路等高可靠性的要求,消息引擎更加註重穩定可靠性,而非僅僅追求高吞吐量。采用了長連接拉模式,在消息實時傳輸方面與推模式不相上下。
  • 第三階段:采用了流式消息處理的消息存儲系統,它能夠實時地接收、存儲和處理大量的數據流。這種消息存儲系統可以快速地處理高吞吐量的消息,並保證數據的順序性和可靠性。

通過流式消息處理,系統能夠實時地對數據流進行分析、計算和轉換,從而實現實時監控、實時決策和實時反饋。 ## 低延遲可用性探索

隨著Java語言生態系統的完善和JVM性能的不斷提升,C和C 不再是低延遲場景下的唯一選擇。在這種背景下,接下來將重點介紹RocketMQ在低延遲和可用性方面的一些探索和創新。

截至至今,我們公司的核心文檔業務依然以高性能、低延遲的消息引擎RocketMQ為主。

低延遲與可用性

應用程序的性能指標通常從吞吐量延遲兩個方面進行評估。

  • 低延遲:不同應用場景對於低延遲的要求有所不同,例如在聊天應用中,低延遲可以定義為200毫秒以內,而在交易系統中,可能要求達到10毫秒以內的延遲。
  • 吞吐量:延遲受多個因素的影響,包括CPU性能、網絡速度、內存使用以及操作系統的優化等。

吞吐量和Little’s Law定理的關系

Little’s Law是由約翰·D·利特爾(John D. Little)提出的一個基本定理,它描述了一個穩定系統中平均流動時間與系統中平均存儲量之間的關系,這個定理對於評估和優化系統的性能非常有用。

該定理可以用簡潔的公式來表達:

L = λW

  • L:系統中平均存儲量(或者說隊列長度)
  • λ:單位時間內進入系統的平均請求量(也稱為流入率)
  • W:一個請求在系統中的平均逗留時間(也稱逗留時間)。

Little’s Law告訴我們,當進入系統的流量(λ)和平均逗留時間(W)保持不變時,系統中的平均存儲量(L)也會保持不變。

註意:當延遲變高時,駐留在分佈式系統中的請求會劇增( L變大),最終導致某些節點堆積假死不可用,不可用的狀態甚至會擴散至其它節點,造成整個系統的服務能力喪失,這種場景又俗稱雪崩。

低延遲探索之路

為了實現低延遲的消息寫入鏈路,RocketMQ采取了一系列的優化RocketMQ作為一款消息引擎,其最重要的作用之一是實現異步解耦和平滑處理系統的峰值數據。 #### RocketMQ現異步解耦

通過RocketMQ,分佈式應用能夠實現異步解耦,從而實現應用程序的自動擴容和縮容。同時,當高峰數據到來時,大量的消息可以積攢在RocketMQ中,後端程序可以根據自身的消費速度來逐步讀取數據。因此,保證RocketMQ在寫消息鏈路上的低延遲至關重要。 #### 降低寫入消息的延遲

通過降低寫入消息的延遲,我們可以提高消息的實時性和可靠性。

為了實現這一目標,可以采取多種方法,例如優化消息的處理邏輯和網絡通信,調整消息的存儲和傳輸機制等。如,網絡RocketMQ作為一款消息引擎,具有異步解耦和削峰填谷的重要功能。 #### RocketMQ優化延遲調整

對延遲非常敏感,隻能容忍 50ms 內的延遲,在壓測初期RocketMQ寫消息出現了大量50~500ms 的延遲,導致了高峰出現大量的失敗,嚴重影響前端業務。 #### 延遲問題分析

RocketMQ是一款完全由Java語言開發的消息引擎。它使用了自主研發的存儲組件,並通過Page Cache來進行加速和存儲,因此它的性能會受到多個因素的影響,包括JVM、GC、操作系統的內核、Linux內存管理機制以及文件IO等。

下面的圖示展示了一條消息從客戶端發送到最終持久化存儲的過程中,每個環節都可能產生延遲。

然而,通過對線上數據的觀察,發現RocketMQ在寫消息的過程中存在偶發的延遲問題,這些延遲可能高達數秒之久。

為了解決這個問題,可以采取以下優化措施:

##### JVM和GC調優

Java虛擬機在運行過程中會產生多種停頓,其中包括GC(垃圾回收)、取消偏向鎖(RevokeBias)和動態類重定義(RedefineClasses,如AOP)。對應用程序影響最大的是GC停頓。在RocketMQ中,盡可能避免Full GC的觸發,但是Minor GC引起的停頓是難以避免的。

通過大量的測試來幫助應用程序調整GC參數。以下是一些優化策略方案: ###### 關閉偏向鎖(RevokeBias )

在 RocketMQ 中發現取 RevokeBias 產生了大量的停頓,通過-XX:-UseBiasedLocking關閉了偏向鎖特性,此外還可以打印GC停頓時間和停頓原因,從而分析其他的因素。

  • 打印GC停頓時間:可以通過-XX: PrintGCApplicationStoppedTime將 JVM 停頓時間輸出到 GC 日志中
  • 打印GC停頓原因:通過-XX: PrintSafepointStatistics -XX: PrintSafepointStatisticsCount=1輸出具體的停頓原因,並進行針對性的優化。

減少日志IO的控制和日志壓縮

GC日志的輸出會涉及文件IO操作,這可能導致不必要的停頓。

優化方法是將GC日志輸出到tmpfs(內存文件系統)中,但使用tmpfs會消耗額外的內存。為了避免內存的浪費,可以考慮使用-XX: UseGCLogFileRotation選項來實現GC日志的滾動。

  • 設置-XX: UseGCLogFileRotation參數,GC日志將自動進行滾動,以避免日志文件過大而影響性能。
  • 設置-XX:GCLogFileSize參數,指定單個GC日志文件的大小。
  • 設置-XX:NumberOfGCLogFiles參數,指定保存的日志文件數量。

這樣做的好處是可以減小單個GC日志文件的大小,降低文件IO的開銷,同時便於管理和查閱GC日志。 ###### 關閉jstat的特性和功能

GC日志會產生文件IO,JVM會將jstat命令需要的一些統計數據輸出到/tmp(hsperfdata)目錄下,可通過-XX: PerfDisableSharedMem關閉該特性,並使用JMX來代替jstat。 ##### PageCache以及內存優化

受限於Linux的內存管理機制,應用程序在訪問內存時有時會出現高延遲的情況。在Linux中,內存主要可以分為兩種類型,即匿名內存和Page Cache。 ###### 內存申請和分配流程

為了提高性能和效率,系統會盡可能多地使用可用內存來進行緩存。然而,在大多數情況下,服務器的可用內存相對較少。當可用內存較少時,應用程序申請或訪問新的內存頁可能導致內存回收的發生。

當後臺內存回收的速度跟不上內存分配的速度時,就會發生直接回收(Direct Reclaim)的情況。這時,應用程序會被迫等待內存回收完成,從而導致巨大的延遲,如下圖所示:

另一方面,內核也會回收匿名內存頁,匿名內存頁被換出後下一次訪問會產生文件 IO,導致延遲,如下圖所示。

上述兩種情況產生的延遲可以通過內核參數調優加以避免。

- vm.extra_free_kbytes:這是一個用於設置系統中額外可用的空閑內存大小的參數。當系統內存接近飽和狀態時,操作系統會通過回收內存頁面來釋放內存空間。然而,為了確保系統的穩定性和性能,通常會保留一定量的空閑內存。 - 增加vm.extra_free_kbytes的值可以增加系統的可用空閑內存,從而降低內存壓力,但也會占用更多的物理內存資源。 - vm.swappiness:這是一個用於設置內存頁交換行為的參數。Linux操作系統使用頁面交換(paging)機制來處理內存不足的情況。 - 當可用內存低於一定閾值時,操作系統會將一部分內存頁存儲到磁盤的交換分區中,從而釋放出物理內存。
- vm.swappiness參數可以調整系統對交換行為的偏好程度。它的取值范圍是0到100,其中0表示不進行內存頁交換,而100表示盡量進行內存頁交換。 ###### Page Cache的利與弊

Page Cache是一種用於加速文件讀寫的緩存機制,在RocketMQ中發揮著重要的作用,使其具備強大的數據堆積能力。

RocketMQ通過將數據文件映射到內存中的方式,將寫入的消息首先存儲在Page Cache中,並通過異步刷盤模式將消息持久化到磁盤(同時也支持同步刷盤)。

該模式大多數情況讀寫速度都比較迅速,但當遇到操作系統進行臟頁回寫,內存回收,內存換入換出等情形時,會產生較大的讀寫延遲,造成存儲引擎偶發的高延遲。 # 總結歸納

針對這種現象,RocketMQ 采用了多種優化技術,比如內存預分配,文件預熱,mlock 系統調用,讀寫分離等,來保證利用 Page Cache 優點的同時,消除其帶來的延遲。 ## 必學知識點:Little’s Law

Little’s Law在應用於各種領域和場景時都具有廣泛的適用性,包括計算機網絡、排隊系統、供應鏈管理等。它可以幫助我們理解系統中的請求流動行為,並且可以用來優化系統的吞吐量、運營效率和服務質量。

采用-XX: UseGCLogFileRotation選項,並合理設置相關參數,可以優化GC日志的輸出和管理,降低對文件IO的影響,同時避免內存浪費和磁盤空間的占用。