流量高峰时期的性能瓶颈有哪些、以及如何来解决
在高并發大量用戶的場景,系統一般會面臨如下三個挑戰:
1. 日益增長的用戶數量
2. 日漸復雜的業務
3. 急劇膨脹的數據
這些挑戰對于性能優化而言表現為:在保持和降低系統TP95響應時間(指的是將一段時間內的請求響應時間從低到高排序,高于95%請求響應時間的下確界)的前提下,不斷提高系統吞吐量,提升流量高峰時期的服務可用性。
本文主要目標是為類似的場景提供優化方案,確保系統在流量高峰時期的快速響應和高可用。
性能瓶頸場景
在講解如何性能優化之前,有必要先了解下性能瓶頸在哪里,以下我們先從瓶頸(各種性能堵塞場景)講起,明確問題所在,然后我們再根據瓶頸,來看如何來解決方案。
?
1.長請求擁塞瓶頸
這是一種單次請求時延變長而導致系統性能惡化甚至崩潰的惡化模式。
對于多線程服務,大量請求時間變長會使線程堆積、內存使用增加,最終可能會通過如下三種方式之一惡化系統性能:
線程數目變多導致線程之間CPU資源使用沖突,反過來進一步延長了單次請求時間;
線程數量增多以及線程中緩存變大,內存消耗隨之劇增,對于基于Java語言的服務而言,又會更頻繁地full GC,反過來單次請求時間會變得更長;
內存使用增多,會使操作系統內存不足,必須使用Swap,可能導致服務徹底崩潰。
典型惡化流程圖如下圖:
長請求擁塞反模式所導致的性能惡化現象非常普遍,所以識別該模式非常重要。
典型的場景如下:某復雜業務系統依賴于多個服務,其中某個服務的響應時間變長,隨之系統整體響應時間變長,進而出現CPU、內存、Swap報警,在分布式環境下,從而引起連鎖反應,最后造成雪崩的情況。
系統進入長請求擁塞反模式的典型標識包括:被依賴服務可用性變低、響應時間變長、服務的某段計算邏輯時間變長等。
2.多次請求杠桿造成性能瓶頸
客戶端一次用戶點擊行為往往會觸發多次服務端請求,這是一次請求杠桿。
每個服務端請求進而觸發多個更底層服務的請求,這是第二次請求杠桿。每一層請求可能導致一次請求杠桿,請求層級越多,杠桿效應就越大。
在多次請求杠桿反模式下運行的分布式系統,處于深層次的服務需要處理大量請求,容易會成為系統瓶頸。
與此同時,大量請求也會給網絡帶來巨大壓力,特別是對于單次請求數據量很大的情況,網絡可能會成為系統徹底崩潰的導火索。典型惡化流程圖如下圖:
多次請求杠桿所導致的性能惡化現象非常常見。
例如:對于推薦系統,一個用戶列表請求會有多個算法參與,每個算法會召回多個列表單元(商家或者團購),每個列表單元有多種屬性和特征,而這些屬性和特征數據服務又分布在不同服務和機器上面,所以客戶端的一次用戶展現可能導致了成千上萬的最底層服務調用。
對于存在多次請求杠桿反模式的分布式系統,性能惡化與流量之間往往遵循指數曲線關系。這意味著,在平常流量下正常運行服務系統,在流量高峰時通過線性增加機器解決不了可用性問題。所以,識別并避免系統進入多次請求杠桿反模式對于提高系統可用性而言非常關鍵。
3.反復緩存造成性能瓶頸
為了降低響應時間,系統往往在本地內存中緩存很多數據。緩存數據越多,命中率就越高,平均響應時間就越快。為了降低平均響應時間,有些開發者會不加限制地緩存各種數據,在正常流量情況下,系統響應時間和吞吐量都有很大改進。
但是當流量高峰來臨時,系統內存使用開始增多,觸發了JVM進行full GC,進而導致大量緩存被釋放(因為主流Java內存緩存都采用SoftReference和WeakReference所導致的),而大量請求又使得緩存被迅速填滿,這就是反復緩存。反復緩存導致了頻繁的full GC,而頻繁full GC往往會導致系統性能急劇惡化。典型惡化流程圖如下圖:
反復緩存所導致性能惡化的原因是無節制地使用緩存。緩存使用的指導原則是:工程師們在使用緩存時必須全局考慮,精細規劃,確保數據完全緩存的情況下,系統仍然不會頻繁full GC。
為了確保這一點,對于存在多種類型緩存以及系統流量變化很大的系統,設計者必須嚴格控制緩存大小,甚至廢除緩存(這是典型為了提高流量高峰時可用性,而降低平均響應時間的一個例子)。
反復緩存反模式往往發生在流量高峰時候,通過線性增加機器和提高機器內存可以大大減少系統崩潰的概率。
性能優化方式
?
1.水平分割模式
原理和動機
典型的服務端運行流程包含四個環節:接收請求、獲取數據、處理數據、返回結果。在一次請求中,獲取數據和處理數據往往多次發生。在完全串行運行的系統里,一次請求總響應時間滿足如下公式:
一次請求總耗時=解析請求耗時 + ∑(獲取數據耗時+處理數據耗時) + 組裝返回結果耗時
大部分耗時長的服務主要時間都花在中間兩個環節,即獲取數據和處理數據環節。
對于非計算密集性的系統,主要耗時都用在獲取數據上面。獲取數據主要有三個來源:本地緩存,遠程緩存或者數據庫,遠程服務。三者之中,進行遠程數據庫訪問或遠程服務調用相對耗時較長,特別是對于需要進行多次遠程調用的系統,串行調用所帶來的累加效應會極大地延長單次請求響應時間,這就增大了系統進入長請求擁塞反模式的概率。
如果能夠對不同的業務請求并行處理,請求總耗時就會大大降低。例如下圖中,Client需要對三個服務進行調用,如果采用順序調用模式,系統的響應時間為18ms,而采用并行調用只需要7ms。
水平分割模式首先將整個請求流程切分為必須相互依賴的多個Stage,而每個Stage包含相互獨立的多種業務處理(包括計算和數據獲取)。完成切分之后,水平分割模式串行處理多個Stage,但是在Stage內部并行處理。如此,一次請求總耗時等于各個Stage耗時總和,每個Stage所耗時間等于該Stage內部最長的業務處理時間。
水平分割模式有兩個關鍵優化點:減少Stage數量和降低每個Stage耗時。為了減少Stage數量,需要對一個請求中不同業務之間的依賴關系進行深入分析并進行解耦,將能夠并行處理的業務盡可能地放在同一個Stage中,最終將流程分解成無法獨立運行的多個Stage。降低單個Stage耗時一般有兩種思路:1. 在Stage內部再嘗試水平分割(即遞歸水平分割),2. 對于一些可以放在任意Stage中進行并行處理的流程,將其放在耗時最長的Stage內部進行并行處理,避免耗時較短的Stage被拉長。
水平分割模式不僅可以降低系統平均響應時間,而且可以降低TP95響應時間(這兩者有時候相互矛盾,不可兼得)。通過降低平均響應時間和TP95響應時間,水平分割模式往往能夠大幅度提高系統吞吐量以及高峰時期系統可用性,并大大降低系統進入長請求擁塞反模式的概率。
具體案例
例如為用戶提供高性能的優質個性化列表服務,每一次列表服務請求會有多個算法參與,而每個算法基本上都采用“召回->特征獲取->計算”的模式。 在進行性能優化之前,算法之間采用順序執行的方式。伴隨著算法工程師的持續迭代,算法數量越來越多,隨之而來的結果就是客戶端響應時間越來越長,系統很容易進入長請求擁塞反模式。曾經有一段時間,一旦流量高峰來臨,出現整條服務鏈路的機器CPU、內存報警。在對系統進行分析之后,我們采取了如下三個優化措施,最終使得系統TP95時間降低了一半:
算法之間并行計算;
每個算法內部,多次特征獲取進行了并行處理;
在調度線程對工作線程進行調度的時候,耗時最長的線程最先調度,最后處理。
缺點和優點
對成熟系統進行水平切割,意味著對原系統的重大重構,工程師必須對業務和系統非常熟悉,所以要謹慎使用。水平切割主要有兩方面的難點:
并行計算將原本單一線程的工作分配給多線程處理,提高了系統的復雜度。而多線程所引入的安全問題讓系統變得脆弱。與此同時,多線程程序測試很難,因此重構后系統很難與原系統在業務上保持一致。
對于一開始就基于單線程處理模式編寫的系統,有些流程在邏輯上能夠并行處理,但是在代碼層次上由于相互引用已經難以分解。所以并行重構意味著對共用代碼進行重復撰寫,增大系統的整體代碼量,違背奧卡姆剃刀原則。
對于上面提到的第二點,舉例如下:A和B是邏輯可以并行處理的兩個流程,基于單線程設計的代碼,假定處理完A后再處理B。在編寫處理B邏輯代碼時候,如果B需要的資源已經在處理A的過程中產生,工程師往往會直接使用A所產生的數據,A和B之間因此出現了緊耦合。并行化需要對它們之間的公共代碼進行拆解,這往往需要引入新的抽象,更改原數據結構的可見域。
雖然進行代碼重構比較復雜,但是水平切割模式非常容易理解,只要熟悉系統的業務,識別出可以并行處理的流程,就能夠進行水平切割。有時候,即使少量的并行化也可以顯著提高整體性能。
對于新系統而言,如果存在可預見的性能問題,把水平分割模式作為一個重要的設計理念將會大大地提高系統的可用性、降低系統的重構風險。總的來說,雖然存在一些具體實施的難點,水平分割模式是一個非常有效、容易識別和理解的模式。
2.垂直分割模式
原理和動機
對于移動互聯網節奏的公司,新需求往往是一波接一波。基于代碼復用原則,工程師們往往會在一個系統實現大量相似卻完全不相干的功能。伴隨著功能的增強,系統實際上變得越來越脆弱。這種脆弱可能表現在系統響應時間變長、吞吐量降低或者可用性降低。導致系統脆弱原因主要來自兩方面的沖突:資源使用沖突和可用性不一致沖突。
資源使用沖突是導致系統脆弱的一個重要原因。不同業務功能并存于同一個運行系統里面意味著資源共享,同時也意味著資源使用沖突。可能產生沖突的資源包括:CPU、內存、網絡、I/O等。
例如:一種業務功能,無論其調用量多么小,都有一些內存開銷。對于存在大量緩存的業務功能,業務功能數量的增加會極大地提高內存消耗,從而增大系統進入反復緩存反模式的概率。對于CPU密集型業務,當產生沖突的時候,響應時間會變慢,從而增大了系統進入長請求擁塞反模式的可能性。
不加區別地將不同可用性要求的業務功能放入一個系統里,會導致系統整體可用性變低。當不同業務功能糅合在同一運行系統里面的時候,在運維和機器層面對不同業務的可用性、可靠性進行調配將會變得很困難。但是,在高峰流量導致系統瀕臨崩潰的時候,最有效的解決手段往往是運維,而最有效手段的失效也就意味著核心業務的可用性降低。
垂直分割思路就是將系統按照不同的業務功能進行分割,主要有兩種分割模式:
1.部署垂直分割
部署垂直分割主要是按照可用性要求將系統進行等價分類,不同可用性業務部署在不同機器上,高可用業務單獨部署;
2.代碼垂直分割。
代碼垂直分割就是讓不同業務系統不共享代碼,徹底解決系統資源使用沖突問題。
缺點和優點
垂直分割主要的缺點主要有兩個:
增加了維護成本。一方面代碼庫數量增多提高了開發工程師的維護成本,另一方面,部署集群的變多會增加運維工程師的工作量;
代碼不共享所導致的重復編碼工作。
垂直分割是一個非常簡單而又有效的性能優化模式,特別適用于系統已經出現問題而又需要快速解決的場景。部署層次的分割既安全又有效。需要說明的是部署分割和簡單意義上的加機器不是一回事,在大部分情況下,即使不增加機器,僅通過部署分割,系統整體吞吐量和可用性都有可能提升。所以就短期而言,這幾乎是一個零成本方案。對于代碼層次的分割,開發工程師需要在業務承接效率和系統可用性上面做一些折衷考慮。
3.降級模式
原理和動機
降級模式是系統性能保障的最后一道防線。理論上講,不存在絕對沒有漏洞的系統,或者說,最好的安全措施就是為處于崩潰狀態的系統提供預案。從系統性能優化的角度來講,不管系統設計地多么完善,總會有一些意料之外的情況會導致系統性能惡化,最終可能導致崩潰,所以對于要求高可用性的服務,在系統設計之初,就必須做好降級設計。根據作者的經驗,良好的降級方案應該包含如下措施:
在設計階段,確定系統的開始惡化數值指標(例如:響應時間,內存使用量);
當系統開始惡化時,需要第一時間報警;
在收到報警后,或者人工手動控制系統進入降級狀態,或者編寫一個智能程序讓系統自動降級;
區分系統所依賴服務的必要性,一般分為:必要服務和可選服務。必要服務在降級狀態下需要提供一個快速返回結果的權宜方案(緩存是常見的一種方案),而對于可選服務,在降級時系統果斷不調用;
在系統遠離惡化情況時,需要人工恢復,或者智能程序自動升級。
典型的降級策略有三種:流量降級、效果降級和功能性降級。
流量降級是指當通過主動拒絕處理部分流量的方式讓系統正常服務未降級的流量,這會造成部分用戶服務不可用;效果降級表現為服務質量的降級,即在流量高峰時期用相對低質量、低延時的服務來替換高質量、高延時的服務,保障所有用戶的服務可用性;功能性降級也表現為服務質量的降級,指的是通過減少功能的方式來提高用戶的服務可用性。效果降級和功能性降級比較接近,效果降級強調的是主功能服務質量的下降,功能性降級更多強調的是輔助性功能的缺失。
缺點和優點
為了使系統具備降級功能,需要撰寫大量的代碼,而降級代碼往往比正常業務代碼更難寫,更容易出錯。在確定使用降級模式的前提下,工程師需要權衡這三種降級策略的利弊。大多數面向C端的系統傾向于采用效果降級和功能性降級策略,但是有些功能性模塊(比如下單功能)是不能進行效果和功能性降級的,只能采用流量降級策略。對于不能接受降級后果的系統,必須要通過其他方式來提高系統的可用性。
總的來說,降級模式是一種設計安全準則,任何高可用性要求的服務,必須要按照降級模式的準則去設計。對于違背這條設計原則的系統,或早或晚,系統總會因為某些問題導致崩潰而降低可用性。不過,降級模式并非不需要成本,也不符合最小可用原則,所以對于處于MVP階段的系統,或者對于可用性要求不高的系統,降級模式并非必須采納的原則。
4.其他性能優化建議
對于無法采用系統性的模式方式講解的性能優化手段,給出一些總結性的建議:
刪除無用代碼有時候可以解決性能問題,例如:有些代碼已經不再被調用但是可能被初始化,甚至占有大量內存;有些代碼雖然在調用但是對于業務而言已經無用,這種調用占用CPU資源。
避免跨機房調用,跨機房調用經常成為系統的性能瓶頸,特別是那些偽batch調用(在使用者看起來是一次性調用,但是內部實現采用的是順序單個調用模式)對系統性能影響往往非常巨大。
以下是架構進階資料,需要學習免費課程的狂戳。
總結
以上是生活随笔為你收集整理的流量高峰时期的性能瓶颈有哪些、以及如何来解决的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WebSocket与消息推送
- 下一篇: AltiumDesigner生成彩色电路