?

高并發系統性能優化及其在電子資料平臺中的應用

2023-02-26 02:49張淼鑫任新會黨蘭學侯彥娥
關鍵詞:線程日志參與者

張淼鑫, 任新會, 黨蘭學, 侯彥娥

(1.河南大學 計算機與信息工程學院, 河南 開封 475004; 2.河南大學 教務處,河南 開封 475004)

0 引言

從小型門戶網站、聊天系統,到大型的“12306”購票系統和“雙十一”電商的秒殺系統,都體現了高并發系統的迫切需求。除了“雙十一”不斷刷新的交易記錄外,另一個重要看點是各大電商平臺如何處理峰值時刻的高并發情況[1-2]。許多用戶都曾遭遇“系統繁忙”這類異常,說明高并發技術仍然面臨挑戰,具有相當大的提升空間。同樣,電子資料平臺也會面臨各種高并發場景。例如,隨著在線學習的普及,當老師在某一時間吸引大量學生時,學生們需要同時訪問不同的課程材料、教科書或講義,這就構成了大規模的高并發讀取場景[3]。

早期典型的架構設計基于經典的技術棧進行開發,但是因為缺乏模塊化設計,受單機性能制約較大,導致高并發的處理性能較差[4]。隨著硬件性能的大幅提升,在對單機進行最優設計的前提下,目前架構設計普遍采取的方法是對系統進行橫向擴展,搭建服務器集群,對合法的請求進行分流,并通過使用消息隊列對請求進行“削峰填谷”的處理。為使數據庫穩定地處理這些請求,往往利用緩存中間件減少對數據庫的訪問,并通過服務降級,減輕峰值期間的訪問壓力[5-6]。本文基于多級緩存、熱點探測、分布式鎖、分布式事務等技術,設計了一套高并發解決方案,并將該方案應用于電子資料系統進行測試。測試結果表明,該方案可為電子資料系統提供卓越的穩定性和可擴展性。

1 技術原理和系統總體架構設計

本文設計的高并發電子資料平臺,主要支持文檔收集、整理、檢索、共享、閱讀、下載、統計分析等主要功能,以及人員信息維護、部門信息維護、權限維護等輔助的系統功能。系統架構如圖1所示,核心組成部分包括文檔管理模塊、檢索與查詢模塊、共享與權限模塊、用戶管理模塊、數據統計與分析模塊。為支持高并發和大規模數據處理,系統引入了多級緩存,包括本地緩存、分布式緩存等,以減輕數據庫負載;采用熱點探測算法,以識別熱門文檔,減少系統瓶頸;使用分布式數據庫和分布式事務處理,以確保數據的一致性和可靠性; 應用分布式鎖機制,以協調多個并發操作,避免數據沖突。

圖1 系統架構

2 高并發系統的實現

2.1 多級緩存與熱點數據的處理

目前在高并發架構中使用較多的是分布式緩存,但是數據需要從遠程緩存中獲取,導致吞吐量下降,所以本系統使用的是多級緩存[7]。多級緩存主要分為3層,Nginx應用層緩存,Redis分布式緩存以及Tomcat的堆內緩存。緩存流程如圖2所示。

圖2 多級緩存流程圖

Nginx集群分為接入和應用兩種,通過接入Nginx將請求分發到應用Nginx。為了提升緩存的命中率,這里的負載均衡算法在正常情況下采用的是一致性哈希算法,但是如果訪問量到達了極限,就降級為輪詢算法。接著在應用Nginx中讀取本地緩存,本地緩存用Lua Shared Dict實現,并結合頁面模板生成頁面。如果Nginx本地緩存已經過期,那么系統將嘗試從Redis中獲取,同時更新Nginx的本地緩存。如果Redis中的數據被LRU(least recently used)算法清理掉,那么系統將發出HTTP(hypertext transfer protocol)請求到后端服務,數據生成服務首先在本地Tomcat的JVM(Java virtual machine)堆棧內存中查找,使用Ehcache緩存。如果JVM堆棧內存中的數據也被LRU清理掉,那么系統將重新請求源頭服務以獲取數據,然后再次更新Tomcat堆內存緩存和Redis緩存,并將數據返回給Nginx,Nginx再將數據緩存到本地。其中各級緩存的目標不同,由于Nginx本地緩存的容量有限,所以該級緩存用于支持對熱點數據的高并發訪問;Redis分布式緩存容量很大,可以支持海量的緩存數據,所以重點針對高離散數據的訪問;最后一級Tomcat堆內緩存,主要用于對抗Redis大規模宕機,大量請求涌入數據生產服務時產生的壓力。

系統除了要處理一些常用的熱點數據之外,更重要的是要處理特殊場合出現的熱點數據。常見的熱點數據會被提前放入多級緩存,特殊的數據一般要通過分析、計算,在Nginx緩存層進行處理,即對熱點數據的實時監測和追蹤。對于熱點數據的監測,可以使用數據調研,對歷史數據進行分析做出策略,但是這種方法的緩存命中率并不能達到100%[8]。本文使用實時計算對熱點數據進行監測,如圖2所示。Flume框架主要用于海量數據聚合,Flink框架在集群環境中對有邊界或者無邊界的數據流進行計算,可對Flume采集的Nginx層日志信息處理分析。用Flume直接對接實時計算框架,如果數據采集速度遠大于處理速度的話,會造成消息丟失或堆積,所以在它們之間引入消息中間件,不僅可在業務邏輯上進行解耦,而且相當于對數據進行了分桶處理,進行數據隔離。這里集成Flume和Kafka完成日志處理,隨后使用Flink實時處理技術,統計某一時間內的文件訪問量、點擊率,從而實現目標的解析——將某一時刻訪問量大的實體數據標識為熱點數據。當這些數據被識別出來后,根據數據自身的特點將其放入多級緩存的各個節點。除此之外,為應對大流量的沖擊,需要對Tomcat集群,也就是該數據所對應的業務處理服務進行擴充。本系統使用Kubernetes,程序自動調整參數值,對其相關服務擴容。

2.2 分布式鎖

高并發系統中另一個重要的問題,是多線程場景下對并發數據進行更改和讀取。在“先更新數據庫、再更新緩存”的設計方案中,存在兩個線程,即線程 A 和線程 B,它們同時操作同一條數據時可能出現以下情況:線程 A 更新數據庫(X=1),線程 B 更新數據庫(X=2),接著線程B更新了緩存(X=2),而線程 A 也更新了緩存(X=1)。最終在緩存中的值為1,而在數據庫中的值為2,導致數據不一致。同樣,使用“先更新緩存,再更新數據庫”的方法也可能面臨類似的問題。本系統通過加分布式鎖解決此問題。傳統的設計方案是用MySQL做分布式鎖,但是MySQL做鎖時要讀取磁盤IO(input output),當訪問量較大時,系統性能會比較低,所以本系統使用Redis作為分布式鎖[9]。

結合業務流程設置分布式鎖,將Redis的相關節點傳入對象,調用該對象的trylock(waitTime, leaseTime, unit)對其加鎖,其中waitTime代表請求鎖的等待時間,leaseTime代表鎖的有效期,unit 代表時間單位。以下為嘗試獲取鎖的偽代碼示例:

Public boolean trylock(waitTime, leaseTime, unit){

newLeaseTime = getLeaseMillis(leaseTime);

remainTime = getRemainMillis(waitTime);

lockWaitTime = calcLockWaitTime(remainTime);//每個等待實例時間與1比較取最大值

failedLocksLimit = failedLocksLimit();//允許獲取鎖失敗的次數

for(ListIterator iterator = locks.listIterator()){//循環每個redis客戶端去獲取鎖

lockAcquired = lock.tryLock();

If(lockAcquired)

acquiredLocks.add(lock);//獲取到該鎖了,加入隊列

failedLocksLimt = failedLocksLimit();//獲取鎖失敗重試機制

}

rFuture.syncUninterruptibly();//key過期后用中斷方式釋放期鎖

}

算法依次在N個實例上嘗試獲取鎖,使用相同的鍵(key)和隨機值,當客戶端將鎖設置到Redis時,再設定網絡連接和響應的超時時間。超時時間應短于鎖的自動失效時間,以避免客戶端陷入等待服務器端Redis響應的情況(即使服務器端的Redis已經失效)。如果服務器端未在規定時間內響應,客戶端應該盡快嘗試另一個Redis實例??蛻舳丝梢酝ㄟ^將當前時間減去獲取鎖時的起始時間,計算獲取鎖所用的時間。只有當超過一半的Redis節點成功獲取了鎖,并且所用時間短于鎖的失效時間時,才被認為成功獲取了鎖。如果成功獲取了鎖,那么鍵的實際有效時間則等于有效時間減去獲取鎖所用的時間;如果由于某些原因未能成功獲取鎖,在至少N/2+1個Redis實例中未能獲取鎖,或者獲取鎖所用時間已經超過了有效時間,則客戶端應該在所有Redis實例上執行解鎖操作(即使在某些Redis實例上根本沒有成功加鎖)。圖3是加鎖過程的流程圖。

圖3 加鎖流程圖

2.3 分布式事務

分布式文件系統中的一種數據操作過程,其中一個請求可能需要調用多個服務,并在這些服務中的每一個都連接到各自的數據庫。所以當其中某一個事務執行失敗,就會出現數據不一致的問題。傳統的解決方發是采用“兩階段”提交 ,但是“兩階段”提交中只有協調者設置了超時機制,而參與者沒有設置,所以當協調者宕機,接受消息的參與者也宕機時,事務的狀態就不確定了[10]。所以本系統使用的是“三階段”提交,在參與者中也引入超時機制,并且在第一階段和第二階段之間引入一個中間狀態解決該問題,圖4是“三階段”提交的狀態機。

圖4 “三階段”狀態機

這里將提交分為3個階段:①CanCommit,②PreCommit,③DoCommit;角色為協調者和參與者。在CanCommit階段,協調者開始寫本地日志,進入WAIT狀態,同時向參與者發送VOTE_REQUEST消息并等待參與者的響應,參與者會響應VOTE_ABORT或者VOTE_COMMIT消息。在PreCommit階段,協調者會通知參與者準備提交或者取消事務,并寫REDO和UNDO日志,但不做提交。此時如果協調者接收到參與者的VOTE_ABORT消息,會寫GLOBAL_ABORT日志,進入ABORT狀態,并向參與者發送GLOBAL_ABORT消息。如果收到的是參與者發送的VOTE_COMMIT消息,協調者將會寫PREPARE_COMMIT日志,并進入PRECOMMIT狀態,再向所有的參與者發送PREPARE_COMMIT消息,之后等待并接收確認消息,一旦收到GLOBAL_ABORT,則寫日志流程結束,不進入下一階段,如果收到PREPARE_COMMIT,則進入DoCommit階段。該階段協調者會向所有的參與者發送GLOBAL_COMMIT消息,并接收參與者的GLOBAL_COMMIT確認消息,寫END_TRANSACTION日志流程結束,如果其中參與者無法接收到來自協調者的請求,會在超時等待后,繼續進行任務提交。

3 系統性能測試

將本文設計的高并發解決方案應用于電子資料系統,并使用JMeter執行教學資源訪問的性能負載測試,通過逐漸增加每秒并發請求數,評估系統的整體性能。結果發現,在不同請求數量的并發負載下,系統始終保持相對穩定,具有良好的穩定性和準確性。表1是不同并發數的系統性能測試數據,通信消耗時間約為2 ms,即使在有10 000個并發訪問時,服務調用時間的平均值也保持在300 ms以下。隨著并發量的增加,系統沒有出現任務異常,可見該高并發設計解決方案在處理大流量式數據時,在數據處理平均耗時和延遲時間等方面表現出了理想的性能。這一性能表現可以滿足大部分企業的數據處理需求。

表1 系統性能測試表

4 結束語

高并發讀寫場景的有效處理是一項具有挑戰性的任務,對于在線教學至關重要。通過合適的系統設計和技術選擇,能夠確保系統穩定處理大規模的并發請求,在電子教學資料系統中提供高效的教學資源訪問,為學生和教育工作者提供良好的學習和教學體驗。需要綜合考慮緩存、負載均衡、數據庫優化、CDN(content delivery network)等策略,以確保系統性能和數據一致性,為在線教學提供更多便利。

猜你喜歡
線程日志參與者
休閑跑步參與者心理和行為相關性的研究進展
一名老黨員的工作日志
扶貧日志
淺析打破剛性兌付對債市參與者的影響
游學日志
淺談linux多線程協作
海外僑領愿做“金絲帶”“參與者”和“連心橋”
一種基于粗集和SVM的Web日志挖掘模型
常數輪理性秘密分享機制
基于上下文定界的Fork/Join并行性的并發程序可達性分析*
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合