1. 程式人生 > 其它 >高併發實現的三板斧:快取、限流和降級。

高併發實現的三板斧:快取、限流和降級。

架構之高併發:快取

  • 快取在高併發系統中有者極其廣闊的應用,需要重點掌握。
    隨著網際網路的普及,內容資訊越來越複雜,使用者數和訪問量越來越大,應用伺服器和資料庫伺服器所做的計算也越來越多。
    但是應用伺服器資源是有限的,資料庫每秒能接受的請求次數也是有限的(或者檔案的讀寫也是有限的)。
    如何能夠有效利用有限的資源來提供儘可能大的吞吐量? 一個有效的辦法就是引入快取。
    部分請求可以從快取中直接獲取目標資料並返回,從而減少計算量,有效提升響應速度,讓有限的資源服務更多的使用者。
    
  • 關鍵詞:命中率 = 命中數 / (命中數 + 沒有命中數)
    1. 業務場景和業務需求
      快取通常適合讀多寫少的業務場景,反之的使用意義並不大,命中率會很低。
      業務需求也決定了實時性的要求,直接影響到過期時間和更新策略,實時性要求越低越適合快取。
      
    2. 快取的設計(策略和粒度)
      通常情況下快取的粒度越小,命中率越高。
      比如說快取一個使用者資訊的物件,只有當這個使用者的資訊發生變化的時候才更新快取,而如果是快取一個集合的話,集合中任何一個物件發生變化都要重新更新快取。 
      當資料發生變化時,直接更新快取的值比移除快取或者讓快取過期它的命中率更高,不過這個時候系統的複雜度過高。
      
    3. 快取的容量和基礎設施
      快取的容量有限就會容易引起快取的失效和被淘汰。
      採用快取的技術選型也是至關重要的,比如採用本地內建的應用快取,就比較容易出現單機瓶頸。而採用分散式快取就更加容易擴充套件。
      所以需要做好系統容量規劃,系統是否可擴充套件。
      
  • 快取應用和實現
    • 在目前的應用服務框架中,比較常見的,時根據快取雨應用的藕合度,分為local cache(本地快取)和remote cache(分散式快取)。
      本地快取:指的是在應用中的快取元件,其最大的優點是應用和cache是在同一個程序內部,請求快取非常快速,
      沒有過多的網路開銷等,在單體應用不需要叢集支援或者叢集情況下各節點無需互相通知的場景下使用本地快取較合適;
      同時,它的缺點也是應為快取跟應用程式耦合,多個應用程式無法直接的共享快取,各應用或叢集的各節點都需要維護自己的單獨快取,對記憶體是一種浪費。 
      
      分散式快取:指的是與應用分離的快取元件或服務,其最大的優點是自身就是一個獨立的應用,與本地應用隔離,多個應用可直接的共享快取。
      

架構之高併發:限流

  • 每個系統都有服務的上限,所以當流量超過服務極限能力時,系統可能會出現卡死、崩潰的情況,所以就有了降級和限流。
  • 限流其實就是:當高併發或者瞬時高併發時,為了保證系統的穩定性、可用性,系統以犧牲部分請求為代價或者延遲處理請求為代價,保證系統整體服務可用
  • 方案一:令牌桶方式(Token Bucket)
    令牌桶演算法是網路流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種演算法。
    先有一個木桶,系統按照固定速度,往桶裡加入Token,如果桶已經滿了就不再新增。
    當有請求到來時,會各自拿走一個Token,取到Token 才能繼續進行請求處理,沒有Token 就拒絕服務。
    
    • 舉例:Guava RateLimiter - 平滑突發限流(SmoothBursty)
    • 舉例:Guava RateLimiter - SmoothWarmingUp
  • 方案二:漏桶方式
    水(請求)先進入到漏桶裡,漏桶以一定的速度出水(介面有響應速率),當水流入速度過大會直接溢位(訪問頻率超過介面響應速率),然後就拒絕請求,可以看出漏桶演算法能強行限制資料的傳輸速率。
    
    • 令牌桶和漏桶對比
      令牌桶是按照固定速率往桶中新增令牌,請求是否被處理需要看桶中令牌是否足夠,當令牌數減為零時則拒絕新的請求; 
      漏桶則是按照常量固定速率流出請求,流入請求速率任意,當流入的請求數累積到漏桶容量時,則新流入的請求被拒絕; 
      令牌桶限制的是平均流入速率(允許突發請求,只要有令牌就可以處理,支援一次拿3個令牌,4個令牌),並允許一定程度突發流量; 
      漏桶限制的是常量流出速率(即流出速率是一個固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),從而平滑突發流入速率; 
      令牌桶允許一定程度的突發,而漏桶主要目的是平滑流入速率; 
      兩個演算法實現可以一樣,但是方向是相反的,對於相同的引數得到的限流效果是一樣的。
      
  • 方案三:計數器方式
    計數器限流演算法也是比較常用的,主要用來限制總併發數,比如資料庫連線池大小、執行緒池大小、程式訪問併發數等都是使用計數器演算法。也是最簡單粗暴的演算法。
    
    • 採用AtomicInteger
      使用AomicInteger來進行統計當前正在併發執行的次數,如果超過域值就簡單粗暴的直接響應給使用者,說明系統繁忙,請稍後再試或其它跟業務相關的資訊。
      弊端:使用 AomicInteger 簡單粗暴超過域值就拒絕請求,可能只是瞬時的請求量高,也會拒絕請求。
      
    • 採用令牌Semaphore
      使用Semaphore訊號量來控制併發執行的次數,如果超過域值訊號量,則進入阻塞佇列中排隊等待獲取訊號量進行執行。如果阻塞佇列中排隊的請求過多超出系統處理能力,則可以在拒絕請求。
      相對Atomic優點:如果是瞬時的高併發,可以使請求在阻塞佇列中排隊,而不是馬上拒絕請求,從而達到一個流量削峰的目的。
      
    • 採用ThreadPoolExecutor java執行緒池
      固定執行緒池大小,超出固定先執行緒池和最大的執行緒數,拒絕執行緒請求;
      

架構之高併發:降級和熔斷

  • 在高併發環境下,服務之間的依賴關係導致呼叫失敗,解決的方式通常是: 限流->熔斷->隔離->降級, 其目的是防止雪崩效應
    當用戶請求 A、P、H、I 四個服務獲取資料時,在正常流量下系統穩定執行,
    如果某天系統進來大量流量,其中服務 I 出現 CPU、記憶體佔用過高等問題,結果導致服務 I 出現延遲、響應過慢,
    隨著請求的持續增加,服務 I 承受不住壓力導致內部錯誤或資源耗盡,一直不響應,
    此時更糟糕的是其他服務對 I 有依賴,那麼這些依賴 I 的服務一直等待 I 的響應,也會出現請求堆積、資源佔用,慢慢擴散到所有微服務,引發雪崩效應。
    
  • 基本的容錯模式
    • 主動超時:Http請求主動設定一個超時時間,超時就直接返回,不會造成服務堆積
    • 限流:限制最大併發數
    • 熔斷:當錯誤數超過閾值時快速失敗,不呼叫後端服務,同時隔一定時間放幾個請求去重試後端服務是否能正常呼叫,如果成功則關閉熔斷狀態,失敗則繼續快速失敗,直接返回。(此處有個重試,重試就是彈性恢復的能力)
    • 隔離:把每個依賴或呼叫的服務都隔離開來,防止級聯失敗引起整體服務不可用
    • 降級:服務失敗或異常後,返回指定的預設資訊

架構之高可用:負載均衡

  • 負載均衡(Load Balance),意思是將負載(工作任務,訪問請求)進行平衡、分攤到多個操作單元(伺服器,元件)上進行執行。是解決高效能,單點故障(高可用),擴充套件性(水平伸縮)的終極解決方案
  • 比如:一臺機器不能滿足,則增加兩臺或者多臺機器,共同承擔訪問壓力。這就是典型的叢集和負載均衡架構:如下圖:
  • 負載均衡的方式
    • 應用叢集:將同一應用部署到多臺機器上,組成處理叢集,接收負載均衡裝置分發的請求,進行處理,並返回相應資料。
    • 負載均衡裝置:將使用者訪問的請求,根據負載均衡演算法,分發到叢集中的一臺處理伺服器。(一種把網路請求分散到一個伺服器叢集中的可用伺服器上去的裝置)
  • 負載均衡的作用(解決的問題):
    1. 解決併發壓力,提高應用處理效能(增加吞吐量,加強網路處理能力);
    2. 提供故障轉移,實現高可用;
    3. 通過新增或減少伺服器數量,提供網站伸縮性(擴充套件性);
    4. 安全防護;(負載均衡裝置上做一些過濾,黑白名單等處理)
  • 負載均衡分類
    • 根據實現技術不同,可分為DNS負載均衡,HTTP負載均衡,IP負載均衡,鏈路層負載均衡等