我們一起聊聊軟件架構伸縮性法則
對于大部分商業和政府部門的系統,初始的開發和部署更側重于實現功能的可用性和創新性,而不是可伸縮性。在早期階段,只要系統能夠應對現有的工作負載,開發團隊就會優先考慮引入新功能以提升業務價值。然而,隨著系統的發展,性能和可伸縮性逐漸成為關鍵問題,甚至關乎系統的生存。在這一點上,架構師需負起責任,將系統改造為能夠快速響應并支持伸縮性的架構。
成本和伸縮性之間的關系
對系統進行伸縮的一個核心原則是能夠方便地添加新資源來處理增長的負載。對于很多系統來說,一個簡單而有效的方法是部署多個無狀態服務器實例,并使用負載均衡器在這些實例之間分配請求,如下圖。
圖片
在云平臺部署資源時,成本主要由兩部分構成:
一是每個虛擬機服務器實例的部署成本
二是負載均衡器的成本,后者取決于新的和活躍的請求數量以及處理的數據量。隨著請求量的增加,已部署的虛擬機需要具備更高的處理能力,導致成本上升。
同時,負載均衡器的費用也會隨著請求和處理的數據量的增加而增長。因此,成本的增加與系統規模的擴大是相互影響的,可伸縮性設計的選擇將不可避免地影響到部署成本。忽略這個因素可能導致意外的高昂費用。
為了控制成本,主要有兩個策略:采用彈性負載均衡器自動根據實際請求量調整服務器實例的規模;以及提升每個服務器實例的處理能力,通常通過優化服務器配置(如線程數量、連接數量、堆內存大小等)實現。通過精心調整這些參數,可以顯著提升性能和處理能力,進而降低成本。
注意系統瓶頸
對一個系統進行伸縮本質上就是要增加它的容量。在上面的示例中,我們通過部署更多的服務器實例來提高請求處理能力。
但是,軟件系統是由多個相互依賴的處理元素或微服務組成的,所以在增加一部分微服務容量的同時,不可避免地會被其他一些微服務拖累。在我們的負載均衡示例中,假設服務器實例都連接到同一個共享數據庫。隨著部署服務器數量的增加,數據庫的請求負載也隨之增加 (如下圖)。
圖片
達到一定階段時,數據庫性能會成為限制因素,導致訪問速度明顯下降。
這時,即便增加服務器的處理能力,也無法從根本上解決問題,因為問題出在數據庫上。要想實現進一步的系統擴展,就必須增強數據庫的處理能力。這可以通過優化查詢語句、增配CPU或內存資源、執行數據庫復制或分片等多種方式來實現。
當然,還有許多其他方法可以緩解這個問題。系統內的任何共享資源都可能變成性能瓶頸。在增加系統的某個部分的能力時,必須考慮到對下游部分的影響,避免因增強而引起系統的其他部分突然承受不住壓力,這種情況可能會導致連鎖反應,進而使整個系統崩潰。數據庫、消息隊列、網絡連接的長時間延遲、線程及連接池和共享的微服務等,都是潛在的性能瓶頸所在。一旦面臨高流量負載,這些瓶頸點很快就會暴露出來。因此,關鍵在于一旦瓶頸出現,能夠防止系統突然崩潰,并能迅速擴展系統能力以應對。
慢服務比故障服務更有害
在正常情況下,系統應該能夠為微服務和數據庫提供穩定、低延遲的通信。當系統負載保持在正常的配置水平時,性能是可預測、一致和快速的,如下圖所示。
圖片
當客戶端的請求量超出常規范圍時,微服務架構中服務間的請求響應時間會開始延長。這尤其明顯當進入的請求負荷超過了某個特定服務(例如服務B)的處理能力時,這時未處理完的請求就會在前置微服務(例如服務A)中累積。因為下游服務的處理速度減緩,導致這個微服務接收到的請求量超過了它能夠完成的請求量。
圖片
當服務因為波動或資源耗竭面臨壓力過大而無法正常響應客戶端請求時,客戶端會經歷延遲,這種情況可能引起連鎖反應,即級聯故障——一個響應緩慢的服務導致沿請求鏈路的請求積壓,進而可能造成整個系統的崩潰。
為了防止這種級聯故障,可以采用一些架構模式,例如回路斷路器和隔板。回路斷路器在檢測到服務延遲超過預設閾值時,可以自動減少請求流向該服務,或完全切斷對其的請求,以防止系統過載。隔板則通過隔離下游服務的故障,保護上游服務不受影響,從而在一個服務出現問題時,避免整個系統受損。這些策略有助于構建出更加彈性和可擴展的系統架構。