我在 Amazon 的服務框架團隊已服務多年。本團隊撰寫有助 AWS 服務 (例如 Amazon Route 53 和 Elastic Load Balancing) 擁有者的工具,協助他們加速建立服務,服務用戶端呼叫服務也更簡便。還有其他 Amazon 團隊為服務擁有者提供計量、驗證、監控、產生用戶端程式庫及產生文件等功能。各服務團隊無須手動整合這些功能到服務當中,而是由服務框架團隊一次進行整合。並透過組態對外推出各項服務的功能。

我們面臨的其中一項挑戰是判斷如何提供合理的預設值,尤其是效能或可用性相關的功能。例如,要設定預設的用戶端逾時並不容易,因為我們的框架對於 API 可能的延遲特性毫無頭緒。即使由服務擁有者或用戶端本身著手也不見得比較容易,經過我們不斷嘗試後,終於獲得一些實用的見解。

有一個共通的問題讓我們困擾不已,就是判斷伺服器同一時間允許對用戶端開放的連線數量。此設計能夠避免伺服器承擔過量工作,進而發生過載。更具體來說,我們希望能夠按負載平衡器最大連線數的比例,進行伺服器的最大連線設定。這是 Elastic Load Balancing 之前的情況,當時硬體負載平衡器受到廣泛使用。

我們的工作是協助 Amazon 服務擁有者和服務用戶端找出負載平衡器上設定的最大連線理想值,並在我們所提供的框架中設定相應值。我們認為,一旦想通如何利用人腦的判斷力做出選擇,就能寫出模擬該判斷的軟體。

然而,最後發現判斷理想值的挑戰性實在太大。最大連線設定過低時,負載平衡器可能會中斷增加的請求數,即使服務仍有充足的容量。最大連線設定過高時,伺服器的速度又會變慢且無回應。根據工作負載設定適當的最大連線時,工作負載會移轉或者相依性效能會改變。然後,設定值再次錯誤,因而造成不必要的失敗和過載。

最後,我們發現最大連線的概念實在不夠準確,無法針對問題提供完善的解答。在本文當中,我們會說明其他取向,例如我們認為可發揮不錯效果的負載卸除。

過載剖析

在 Amazon,我們透過設計系統主動擴展的方式,在遭遇過載情境之前避免它。但是,保護系統涉及架構層的保護。從自動調整規模開始之外,還包括適當卸除過大負載的機制、機制監控能力,以及最重要的持續測試。
 
我們對服務進行負載測試之後,發現低使用率時的伺服器延遲,比高使用率時要慢。在負載沈重時,執行緒爭用、環境切換、垃圾收集及 I/O 爭用都會變得更明顯。最終,服務會達到效能開始急速下降的反曲點。
 
此觀察背後的理論稱為 通用比例定律,衍生自 阿姆達爾定律。這套理論指出,可透過並行化提高系統輸送量時,終究會受限於序列化點的輸送量 (也就是指受限於無法並行化的任務)。
 
糟糕的是,不只輸送量受到系統資源的限制,輸送量通常也會在系統超載時下降。給予系統的工作量超過其資源能夠承擔的範圍時,系統就會變慢。電腦即使過載仍會繼續接收工作,但是進行環境切換所耗費的時間會增加,且速度變慢,因此難以正常運作。
 
在用戶端與伺服器交談的分散式系統中,用戶端通常會並在一段時間後失去耐性,並停止等候伺服器回應。這段持續時間稱為逾時。伺服器嚴重過載使延遲超過用戶端逾時設定時,請求就會開始失敗。下圖顯示的是伺服器回應時間如何隨著提供的輸送量 (單位為每秒交易量) 增加而延長,使回應時間最終達到效能急速惡化的反曲點。

在上圖中,當回應時間超過用戶端逾時設定時效能明顯變差,但圖中無法看出狀況到底有多差。為了具體說明,我們沿著延遲繪製了用戶端感知可用性。我們沒有使用常見的回應時間測量值,而是改用回應時間中位數。回應時間中位數表示有百分之 50 的請求比中位數來得快。若服務的延遲中位數等於用戶端逾時,表示有一半的請求逾時,因此可用性為百分之 50。在這個時刻,延遲增加從延遲問題,正式轉為可用性問題。以下是發生的狀況圖:

很可惜,這張圖不易閱讀。要描述可用性問題,比較簡單的方式是區別實際處理量輸送量。輸送量是每秒傳送至伺服器的總請求數。實際處理量則是輸送量當中處理未出錯,且延遲較短,回應能夠讓用戶端順利使用的部分。

正向回饋迴圈

過載情境最令人髮指之處,在於回饋迴圈中它如何自我放大。一旦用戶端逾時,用戶端收到錯誤訊息已經夠糟了。更糟糕的是,伺服器目前為止針對該請求的一切進度都浪費了。在容量受限的過載情境中,系統千萬不該浪費工作。

用戶端經常重試請求,更讓事情雪上加霜。這樣做會導致提供給系統的負載加倍。如果在服務導向架構中的呼叫圖深度足夠 (也就是說,用戶端呼叫服務,該服務呼叫其他服務,其他服務再呼叫其他服務),且每個架構層重試數次,則最底層過載會導致串聯重試,導致提供的負載指數放大。

種種因素加在一起,過載就會建立自己的回饋迴圈,導致過載進入穩定狀態。

預防工作浪費

在表層卸除負載很簡單。伺服器接近過載時,應該會開始拒絕過量請求,才能專注於決定放行的請求。卸除負載的目標是讓伺服器決定接受的請求保持低延遲,服務才能在用戶端逾時之前回覆。利用此方法,伺服器可針對所接受的請求保持高可用性,且只有過量流量的可用性受到影響。

以卸除過量負載的方式控制延遲,能使系統的可用性更高。但是,此方法的優點不易在上圖中顯現。整體可用性資料線還是向下,看起來不太討喜。然而關鍵在於,伺服器決定接受的請求保持可用,因為它們很快能獲得服務。
即使提供的輸送量增加,卸除負載可讓伺服器繼續保持實際處理量,盡量完成最多請求。但是,卸除負載並非萬靈丹,最終伺服器會成為阿姆達爾定律和實際處理量下滑的犧牲品。

測試

我跟其他工程師聊起卸除負載時,我喜歡提出,如果他們的服務尚未測試到崩潰的負載點,以及遠超出崩潰點的負載量,就該假設在面臨最不樂見的情況時,該服務會故障。在 Amazon,我們花費許多時間對服務進行負載測試。產生如本文先前提供的圖表,有助我們建立過載效能的基準值,讓我們邊改進服務內容,邊追蹤我們一段時間內的表現。

負載測試有多種類型。有些負載測試可確保叢集在負載增加時自動擴展,另外一些測試則使用固定的叢集大小。如果在過載測試中,服務的可用性因為輸送量增加快速降為零,這是好的徵兆,因為可從中得知該服務需要額外的負載卸除機制。理想的負載測試結果是服務在接近完全使用時的實際處理量保持平穩,甚至在輸送量更多時仍持平。

Chaos Monkey 這類的工具,有助針對服務進行混沌工程測試。舉例來說,可讓 CPU 超過負荷或使封包遺漏,模擬過載期間會發生的狀況。我們使用的另一種測試技術是使用現有的負載產生測試或 Canary,針對測試環境驅動持續負載 (而不是逐漸增加的負載),但開始移除該測試環境中的伺服器。這樣做會增加每個執行個體的提供輸送量,因此可測試執行個體輸送量。人為縮小叢集使負載增加的技術,在隔離測試服務時很實用,但無法完全取代完整負載測試。完整的端對端負載測試也會增加該服務相依性的負載,使其他瓶頸顯現出來。

在測試當中,除了伺服器端可用性和延遲外,我們一定會測量用戶端感知的可用性和延遲。當用戶端可用性開始下降時,我們會將負載推升至遠超過該點。若卸除負載生效,即時提供的輸送量增加至遠超過服務的已擴展容量,實際處理量仍可保持穩定。

在探討避免過載的機制之前,過載測試是一大關鍵。每個機制都有其複雜性。例如:考量我在本文開頭提及的所有服務框架內組態選項,以及設定適當的預設值難度有多高。每個避免過載的機制都會添加不同的保護,且效能有限。透過測試,團隊可偵測系統瓶頸,並判斷他們所需的過載處理保護組合。

可見性

在 Amazon,無論我們使用何種技術保護服務、避免過載,我們都會審慎思考這些過載因應措施生效時,我們所需的數據和可見性。

品質不佳保護拒絕請求時,該拒絕會縮減服務的可用性。服務誤解且在仍有容量時拒絕請求 (例如:最大連線數設定過低) 時,即發生誤判。我們努力讓服務的誤判率保持為零。如果團隊發現服務的誤判率經常不是零,表示服務調校到過度敏感的程度,或者個別主機持續合法過載,可能存在擴展或負載平衡問題。在類似這樣的情況下,我們可能有一些應用程式效能有待調校,或可切換為更能適當處理負載失衡的大型執行個體類型。

從可見性的角度來看,當負載卸除拒絕請求時,我們會確保擁有適當的檢測工具,能夠得知用戶端的身分、用戶端呼叫的操作,以及有助我們調校保護措施的其他資訊。我們也會利用警示偵測因應措施是否拒絕顯著流量。一旦遇到品質不佳,我們的優先事項是增加容量並處理目前的瓶頸。

在負載卸除方面,還有一個細微但重要的可見性相關考量。我們已經知道不讓請求失敗延遲污染服務延遲數據的重要性。畢竟,與其他請求相比,負載卸除請求的延遲應該極低。例如,如果服務卸除的負載是百分之 60 的流量,即使該服務成功請求的延遲數字不好看,延遲中位數仍可能非常亮眼,原因是請求快速失敗所以被隱而未報。

負載卸除對自動調整規模和可用區域的效應

若設定不當,負載卸除可能導致回應式自動調整規模失效。請想想下面的範例:有一項服務設定為以 CPU 為基礎的回應式規模調整,並設定負載卸除,會拒絕 CPU 目標類似的請求。在此情況下,負載卸除系統會減少請求數量,讓 CPU 保持低負載,且回應式規模調整從來不會收到或取得啟動新執行個體的延遲訊號。

我們在設定處理可用區域故障的自動調整規模限制時,也仔細考量了負載卸除邏輯。服務會擴展至能與其容量匹配的可用區域無法使用的點,同時保有我們的延遲目標。Amazon 團隊經常檢閱 CPU 等系統數據,推估服務有多接近容量限制。但是,有了負載卸除,與透過系統數據指出的情況相比,叢集的執行能夠更加接近請求會遭到拒絕的點,同時不會佈建多餘的容量去處理可用區域故障一事。有了負載卸除,我們得對服務的破壞點測試更有把握,以瞭解叢集在任何時間點的容量和餘量。

事實上,我們可以利用負載卸除塑造離峰、非關鍵流量,節省成本。例如,如果叢集負責處理 amazon.com 的網站流量,可能會決定搜尋網路爬取程式的流量,不值得為了取得完整的可用區域備援而花費擴展成本。但是,我們面對這套方法的態度非常謹慎。並非所有請求的價值都相同,而且證明服務想要同時為真人流量和卸除過量的網路排取程式流量提供可用區域備援,需要細膩的設計、持續測試和業務方的買帳。若服務的用戶端不清楚服務以此方式設定,用戶端在可用區域故障期間的行為就可能意味著重大關鍵的可用性影響,而不是非關鍵性的負載卸除。基於此,在服務導向架構中,我們會嘗試盡早推動這類塑造方式 (例如從用戶端首次接收請求的服務),而不是嘗試進行擴及整個堆疊的全面性優先順序決策。

負載卸除機制

在討論負載卸除和無法預測情境時,專注於許多造成品質不佳的可預測條件也很重要。在 Amazon,服務會保持足夠的可用區域故障處理額外容量,而不必增加新容量。他們會利用調節確保用戶端之間的公平性。

但是,儘管採用這些保護和操作實務措施,服務在任何時間點時仍有特定的容量值,並且可能因為林林總總的理由而過載。理由包括流量預期外暴增、叢集容量突然損失 (因為部署欠佳或其他原因)、用戶端從提出廉價請求 (如快取讀取) 轉移為昂貴的請求 (如快取失誤或寫入)。服務過載時,必須結束已接受的請求;也就是說,服務必須自我保護,避免品質不佳。在本節的其餘部分,會討論我們多年來管理過載的考量和技術。

瞭解拉掉請求的成本

我們會對服務確實進行超過實際處理量平原點的負載測試。此做法的關鍵理由之一,是為了確保我們在負載卸除期間拉掉請求時,拉掉請求的成本越低越好。我們已經知道意外日誌陳述或通訊端設定很容易遺漏,導致拉掉請求的成本遠高於必要。

在少見的情況下,快速拉掉請求比堅持處理請求的代價更昂貴。若是如此,我們會減緩已拒絕請求的速度,使其 (至少) 符合成功回應的延遲。但是,堅持處理請求的成本盡量壓低時,這樣做相當重要;例如:並未導致應用程式執行緒延遲時。

排定請求優先順序

伺服器過載時,有機會將內送請求分流,決定要接受和拒絕哪些請求。伺服器最重要、應接受的請求是來自負載平衡器的 Ping 請求。若伺服器無法及時回應 Ping 請求,負載平衡器就會停止傳送新請求至該伺服器一段時間,伺服器就會閒置。在品質不佳情境中,我們最不樂見的就是叢集縮小。除了 Ping 請求之外,每項服務的請求優先順序選項不同。

想一想提供資料以呈現 amazon.com 的 Web 服務。與來自真人的請求相比,提供服務給支援搜尋索引網路爬取程式網頁呈現的服務呼叫,可能沒有那麼關鍵。提供服務給網路爬取程式請求固然重要,但理想狀況下,可轉移至離峰時間進行。但是,在像 amazon.com 這樣整合大量服務的複雜環境中,若各項服務使用彼此衝突的優先順序啟發法,全系統的可用性可能受到影響,工作也可能浪費。

優先順序和調節可合併使用,兼顧避免嚴格的調節天花板及服務過載保護。在 Amazon,在我們允許用戶端爆量至超過設定調節上限的情況下,來自這些用戶端的過量請求的優先順序,可能會低於來自其他用戶端的限額內請求。我們花費許多時間著眼於置放演算法,目標是將爆量變成無法使用可能性降到最低,但考量利弊之後,我們偏好可預測的佈建工作負載,更甚於無法預測的工作負載。

緊盯時鐘

如果服務在回應請求過程中發現用戶端已逾時,則它可以跳過其餘工作,並立即讓請求失敗。否則,伺服器將繼續處理該請求,並且其晚到的回應會有如森林中一棵樹的倒下,無人知曉。從伺服器的角度來看,它已經返回了成功的回應。但是從逾時的用戶端看來,這是一個錯誤。

避免這種運算資源浪費的一種方法是讓用戶端在每個請求中包含逾時提示,告訴伺服器它們願意等待多長時間。伺服器可以評估這些提示,並以很少的代價丟棄注定失敗的請求。

此逾時提示可以表示為絕對時間或持續時間。不幸的是,眾所周知,分佈式系統中的伺服器很難約定確切的當前時間。Amazon Time Sync Service 透過在每個 AWS 區域中將 Amazon Elastic Compute Cloud (Amazon EC2) 執行個體的時鐘與一組冗餘的衛星控制原子鐘同步,來解決這個問題。同步良好的時鐘在 Amazon 中對於記錄日誌也很重要。比較時鐘不同步的伺服器上的兩個日誌檔,讓故障診斷變得更加困難。

「看時鐘」的另一種方法是測量一台機器上的持續時間。伺服器擅長在本機測量經過的時間,因為它們無需與其他伺服器達成共識。不幸的是,用持續時間表示逾時也有問題。首先,您使用的計時器必須是單純的,並且在伺服器與網路時間通訊協定 (NTP) 同步時不能倒退。一個更困難的問題是,為了測量持續時間,伺服器必須知道何時啟動碼錶。在某些極端過載的狀況下,大量的請求可能會在傳輸控制通訊協定 (TCP) 緩衝區中排隊,因此,當伺服器從其緩衝區中讀取請求時,用戶端已經逾時。

每當 Amazon 的系統表示用戶端逾時提示時,我們都會嘗試讓它具有可傳遞性。在包含多個躍點的面向服務的架構,我們在每個躍點之間傳播「剩餘時間」期限,以便呼叫鏈末尾的下游服務可以知道多長時間後回應會失效。

伺服器知道用戶端的期限後,就需要確定在服務實作中的哪裡強制執行期限。如果服務擁有請求佇列,則我們可以在將每個請求移出佇列後評估逾時。但這仍然很複雜,因為我們不知道請求可能需要花費多長時間。某些系統會估算 API 請求的處理時間,如果用戶端報告的期限超出了延遲估算值,它們會提早丟棄請求。但是,事情很少那麼簡單。例如,快取命中要比快取遺漏快,並且估算器無法提前知道是命中還是遺漏。或者服務的後端資源可能已分區,並且可能僅某些分區速度緩慢。這時可以採取變通的方法,但這些方法可能會在某些無法預測的情況下適得其反。

根據我們的經驗,儘管復雜並有所妥協,但在伺服器上強制執行用戶端逾仍然比其他方法更好。為避免請求堆積以及伺服器處理可能對任何人都不再需要的請求,我們發現強制執行「每個請求存留時間」並丟棄注定失敗的請求會有所幫助。

完成已經開始的工作

我們不希望浪費任何有用的工作,尤其是在過載的情況下。丟棄工作會建立一個積極的反饋迴圈,從而加劇過載,因為如果服務沒有及時回應,用戶端通常會重試請求。發生這種情況時,一個消耗資源的請求就會變成許多消耗資源的請求,從而使服務的負載倍增。當用戶端逾時並重試時,他們通常會停止等待第一個連線的回應,而在另一個連線中提出新的請求。如果伺服器完成第一個請求並進行回复,則用戶端可能沒有監聽,因為它正在等待重試請求的回复。

正是因為存在這樣的浪費,我們才設計執行有限工作的服務。在需要提供可以返回大型資料集 (或實際上任何清單) 的 API 時,我們使用支援分頁的 API。這些 API 返回部分結果和一個字符,用戶端可以使用該字符請求更多資料。我們發現,當伺服器處理記憶體、CPU 和網路頻寬存在上限的請求時,更容易估計對服務的額外負載。如果伺服器不知道處理一個請求需要什麼,則很難執行許可控制。

劃分請求優先級的一個更微妙的方法是,透過了解用戶端使用服務 API 的方式作出調整。例如,假設服務擁有兩個 API:start()end()。為了完成工作,用戶端需要能夠呼叫這兩個 API。在這種情況下,服務應該優先處理 end() 請求,而不是 start() 請求。如果優先處理 start(),用戶端將無法完成他們已經開始的工作,從而導致運算能力不足。

分頁是需要關注的可能出現浪費的另一個地方面。如果用戶端必須連續發出幾個請求才能對服務的結果進行分頁,並且在第 N-1 頁之後遇到失敗並丟棄結果,那麼 N-2 頁上的服務呼叫以及在執行過程中進行的任何重試將浪費。這表明,像 end() 請求一樣,應將首頁請求的優先級放在後一頁的分頁請求之後。也正是因為如此,我們更需要將服務設計為執行有限的工作,而不是對它們在同步操作期間呼叫的服務進行無休止的分頁。

關注佇列

管理內部佇列時查看請求的持續時間也很有幫助。許多現代服務架構都使用記憶體內佇列來連接執行緒池,以在工作的各個階段處理請求。具有執行程式的 Web 服務框架可能會在其前面設定佇列。使用任何基於 TCP 的服務,作業系統都會為每個通訊端維護一個緩衝區,而這些緩衝區可以存放大量已被壓抑的請求。

當我們將工作從佇列中拉出來時,我們可以檢查這個工作在佇列中存在了多長時間。至少,我們嘗試在我們的服務指標中記錄該持續時間。除了限制佇列的大小外,我們還發現,對傳入的請求在佇列上存在的時間設定上限非常重要,這樣一來,如果存在時間過長,則將其丟棄。這樣就可以釋放伺服器資源,以處理擁有更高成功機率的新的請求。這種方法最極致的作法是,如果協定支援,我們會尋求使用後進先出 (LIFO) 佇列的方法。(在給定 TCP 連線上對請求進行 HTTP/1.1 管道傳輸不支援 LIFO 佇列,但 HTTP / 2 通常支援。)

當服務過載時,負載平衡器也可能使用稱為 surge queues 的功能對傳入的請求或連線進行排隊。這些佇列可能導致資源耗盡,因為當伺服器最終收到請求時,它不知道該請求在佇列中存在了多長時間。通常保險的做法是使用溢出設定,進行快速失敗,而不是讓多餘的請求排隊。在 Amazon,該經驗被移植到了下一代 Elastic Load Balancing (ELB) 服務中。Classic Load Balancer 使用暴衝佇列,但 Application Load Balancer 拒絕多餘的流量。無論設定如何,Amazon 的團隊都會監控服務的相關負載平衡器指標,例如暴衝佇列深度或溢出數。

根據我們的經驗,關注佇列的重要性再強調也不為過。我經常會驚訝地在我依賴的系統和庫中發現記憶體內佇列,在直覺上我根本沒有想到會存在這些佇列。當我深入研究系統時,我發現,假設有一些我尚不了解的佇列會很有幫助。當然,只要我能提出正確的實際測試使用案例,過載測試比深入研究程式碼能夠提供更多有用的資訊。

防止底層過載

服務由幾層組成,從負載平衡器到具有 netfilter iptables 功能的作業系統,到服務框架,再到程式碼,每一層都提供了一些服務保護功能。

像 NGINX 這樣的 HTTP 代理通常支援最大連線數功能 (max_conns),以限制將傳遞給後端伺服器的作用中請求或連線的數量。這可能是一種有用的機制,但根據經驗,我們將其用作最後的手段,而不是預設的保護選項。使用代理時,很難優先處理重要流量,並且原始的進行中的請求計數追蹤有時並不能提供準確的資訊,來判斷服務是否真正超載。

在本文的開頭,我描述了我在服務框架團隊工作時遇到的挑戰。我們試圖為 Amazon 團隊提供在其負載平衡器上設定的最大連線數的預設值建議。最後,我們建議團隊為他們的負載平衡器和代理設定最大連線數,並讓伺服器利用本機資訊實作更準確的負載卸除演算法。但是,最大連線數不超過伺服器上的接聽程式執行緒、接聽處理程序或檔案描述符的數量也很重要,這樣伺服器才能擁有足夠的資源,處理來自負載平衡器的關鍵運作狀態檢查請求。

作業系統擁有限制伺服器資源使用的功能,這些功能強大,可以在緊急狀況下使用。並且由於我們知道可能發生過載,因此應未雨綢繆,確保透過使用正確的 Runbook 並準備好特定命令。iptables 公用程式可以設定伺服器接受的連線數上限,並且可以比任何伺服器處理程序便宜得多的方式拒絕過多的連線。還可以透過它設定更複雜的控制,例如允許以限定的速率建立新連線,甚至為每個來源 IP 地址設定限定的連線速率或連線數量。來源 IP 篩選條件功能強大,但不適用於傳統的負載平衡器。但是,ELB Network Load Balancer 甚至可以透過網路虛擬化在作業系統層保留呼叫者的來源 IP,從而使 iptables 規則 (如來源 IP 篩選條件) 按預期工作。

分層保護

在某些狀況下,耗盡資源的伺服器會不降低速度而拒絕請求。考慮到這一現實,我們考察了伺服器與其用戶端之間的所有躍點,以了解它們如何合作並幫助卸除過多的負載。例如,預設情況下,一些 AWS 服務包含卸除選項。當我們透過 Amazon API Gateway 提供服務時,我們可以設定任何 API 可接受的最大請求速率。當我們透過 API Gateway、Application Load Balancer 或 Amazon CloudFront 提供服務時,我們可以設定 AWS WAF 以在多個維度上卸除過多的流量。

可見性製造了緊張的局勢。儘早拒絕很重要,因為它是避免多餘流量最便宜的方式,但這會影響可見性。這就是我們分層保護的原因:讓伺服器承擔超出其負荷能力的工作,並丟棄過多的部分,並記錄足夠的資訊以了解其正在丟棄的流量。由於伺服器丟棄的流量有限,因此我們依靠它前面的層來保護它,避免極端流量的出現。

以不同的方式思考過載

在本文中,我們討論了由於資源受限和爭用,系統並行工作增加,變得越來越慢,因此需要削減負載。過載反饋迴圈由延遲推動,這最終會導致工作浪費,請求速率放大甚至更高的過載。避免由萬用可擴展定律和阿姆達爾定律驅動的這種力量很重要,方法是卸除過多負載並在面對過載時保持可預測的一致效能。專注於可預測的、一致的效能是建置 Amazon 服務的關鍵設計原則。

例如,Amazon DynamoDB 是一項可提供可預測效能和大規模可用性的資料庫服務。即使工作負載迅速增加並超出了預置資源的負荷,DynamoDB 仍可為該工作負載保持可預測的實際處理量延遲。DynamoDB Auto Scaling自適應容量按需之類的因素會迅速做出反應,以提高實際處理量,來適應工作負載的增加。在此期間,實際處理量保持穩定,並在 DynamoDB 之上的層中保持服務具有可預測的效能,並提高整個系統的穩定性。

對於可預測效能的關注,AWS Lambda 上的範例更多。當我們使用 Lambda 來實作服務時,每個 API 呼叫都在自己的執行環境中執行,該執行環境所分配到的運算資源數量不變,並且一次僅處理這一個請求。這不同於以伺服器為基礎的模式,後者在給定的伺服器上會處理多個 API。

將每個 API 呼叫分配獨立的資源 (計算、記憶體、磁碟、網路) 將在某種程度上繞過阿姆達爾定律,因為一個 API 呼叫的資源不會與另一個 API 呼叫的資源競爭。因此,如果輸送量超過了實際處理量,則實際處理量將保持不變,而不像在較傳統的以伺服器為基礎的環境中那樣出現下降。這不是萬能藥,因為相依性可能會減慢速度並導致並行性增加。但是,在這種狀況下,至少不會出現我們在本文中討論的主機上資源爭用。

這種資源隔離是現代無伺服器計算環境 (如AWS FargateAmazon Elastic Container Service (Amazon ECS) 和 AWS Lambda) 所帶來的某種微妙但重要的好處 。在 Amazon,我們發現要實作負載卸除需要進行大量的工作,從調整執行緒池到選擇完美的設定以實現最大的負載平衡器連線數。這些類型的設定的合理預設值很難或無法找到,因為它們取決於每個系統的獨特運作特性。這些較新的無伺服器運算環境在較低層級提供資源隔離,並在較高層級提供控制手段 (例如調節和並行控制),以防止過載。在某些方面,我們可以不必追求完美的預設設定值,完全避開這些設定,在無需進行任何設定的情況下也可避免各類過載。

進一步閱讀

通用可擴展性定律
阿姆達爾定律
分階段事件驅動架構 (SEDA)
利特爾法則 (描述系統中的並行性以及如何確定分佈式系統的容量)
• Marc 部落格,關於利特爾法則的故事
Elastic Load Balancing 深入探討與最佳實務,re:Invent 2016 上的簡報 (描述了 Elastic Load Balancing 的發展如何阻止過多的請求進入佇列)
• Burgess, Thinking in Promises: Designing Systems for Cooperation, O’Reilly Media, 2015



作者簡介

David Yanacek 是 AWS Lambda 的資深首席工程師。自 2006 年以來,David 一直擔任 Amazon 的軟體開發人員,之前負責 Amazon DynamoDB 和 AWS IoT、內部 Web 服務框架,以及車隊營運自動化系統的開發。David 工作中最喜歡的活動之一是,執行日誌分析並篩選操作指標,以尋找讓系統隨時間推移而執行越來越平穩的方法。

採用抖動的逾時、重試和退避 實作運作狀態檢查 檢測分散式系統的操作可視性