Netflix Play API:我們爲什麼構建了一個演進式架構?

在QCon SF大會上,Suudhan Rangarajan做了題爲“Netflix Play API:我們爲什麼構建了一個演進式架構”的演講。他演講的要點包括:具有單一標識/職責的服務更容易升級;工程師應該在構建服務時花時間識別出需要做的核心決策,並確定這些決策是否是需要經過協商或快速試驗才能做出的“1型”或“2型”決策;使用像”適應性函數(fitness functions)“這樣的工具設計一個“演進式架構”會帶來很多好處。

在演講開始時,Netflix高級軟件工程師Rangarajan談了Netflix在2016年的兩個重要的業務里程碑,這兩個里程碑也產生了巨大的工程影響。第一個是2016年1月發佈的“#netflixeverywhere”,使許多國家的客戶可以在Netflix之前不可用的地方註冊和觀看內容。第二個里程碑是2016年11月發佈的新功能“下載和訪問”(Download & Go),該功能允許用戶將內容下載到設備上進行離線觀看。這兩次發佈都給“Play API”帶來了更大的壓力,“Play API”的任務之一就是啓動面向客戶的內容流。這導致了幾次服務中斷,同時還導致了部署頻率的降低和回滾次數的增加,正如Forsgren、Humble和Kim在他們的著作Accelerate中所述的那樣,回滾是與軟件交付績效相關的關鍵指標。

演講概述了Play API服務以前的體系結構。客戶設備連接到運行在邊緣的API代理服務(通常是Zuul API網關),該服務與包含多個API(包括Play API)的整體式API服務通信。反過來,這個API服務與特定域的微服務通信,以處理用戶的下游請求。

image

演講的其餘部分分爲三個部分,更詳細地討論了與最近增強整體式API服務相關的背景和指導原則。這些部分包括:服務身份——探討服務存在的原因;識別1型和2型決策——確定哪些決策將產生重大的長期影響,像這樣的決策需要大量的前期投資;可演化性——探討如何構建能夠隨需求和約束的變化而演化的服務。

在服務身份部分,Rangarajan建議工程師必須“從爲什麼開始”;詢問服務爲什麼存在以確定其職責。對於Play API,其動機鏈從Netflix希望“引領互聯網電視革命,爲全世界數十億人帶去快樂”,到最大化客戶參與(從註冊到流媒體),再到最終“實現24/7的獲取、發現和播放功能”。他提醒聽衆注意單一責任原則,並提醒他們在執行這一原則時應小心“將多個身份納入單個服務”,因爲這可能導致使用低內聚和高耦合的架構反模式創建服務。因此,Play API團隊提出的第一個重大更改是將現有的整體式API服務劃分爲“單功能API服務”模型。Play API將被重新構建並部署爲一個微服務——這是“1型”決策。

我們相信我們的服務具有簡單的單一身份。該身份關係到公司、組織、團隊及其對等服務的身份,是對它們的補充。

演講的“1型和2型決策”部分首先援引並解釋了Jeff Bezos的決策模型。1型決策非常重要,具有長期的影響,因此,這些決策必須有系統地做出,並與他人進行協商。2型決策很容易改變,沒有長期的影響,因此,這些決策應由“判斷力強的個人或小組”迅速作出。Play API團隊確定的三個1型決策包括適當耦合、同步與異步通信和數據架構。

有些決策非常重要且不可逆轉或幾乎不可逆轉——單向的大門——這些決策的做出必須有條不紊、仔細、緩慢、經過深思熟慮和協商[…]我們可以把這種類型的決策稱爲1型決策……

首先來看適當耦合,Rangarajan指出,在設計基於微服務的架構時,實際上有兩種類型的共享庫:提供公共功能的庫和用於服務間通信的客戶端庫。當使用具有公共功能的共享庫(例如“實用程序包”)時,很容易引入過多的“二進制耦合”,這使得該庫的維護和升級變得具有挑戰性。使用客戶端通信庫還很容易引入“操作耦合”,例如,客戶端庫中提供的良好的回退功能可能會消耗過多的資源並導致級聯失敗。如果上游服務團隊只提供Java客戶端庫,那麼很容易引入與通信庫的“語言耦合”。

這些問題加上對當前需求的識別和討論,使得Play API團隊決定積極工作,儘量減少在他們計劃創建的新服務中使用共享的“實用程序”庫。團隊還決定使用gRPC而不是通過JSON和HTTPS進行服務到服務通信的REST,該框架允許通過Protocol Buffers定義RPC方法和實體,並使用各種語言自動生成客戶端庫/SDK。對於這種“適當耦合”的1型決策,他們的總體建議是“考慮具有雙向通信的‘瘦’自動生成客戶端,並最小化跨服務邊界的代碼重用”。

image

接下來,他討論了2型決策、同步與異步。經過慎重考慮,團隊認定,Play API和支持服務之間除了請求/響應類的交互之外,不需要其他交互,因此,他們實現了一個帶有非阻塞I/O的阻塞請求處理程序,用於發起服務間調用。

image

當討論到團隊遇到的第三種1型決策“數據架構”時,Rangarajan警告說,“如果沒有有意識地設計一個數據架構,數據就會變成一個龐大的整體”。在之前的Play API架構中,多個服務訪問同一個數據源,這導致了高耦合,並減少了服務和底層數據模式的演進路徑。他故意一笑,並引用David Wheeler的話說,“計算機科學的所有問題可以通過另一個間接層來解決”,他還指出,在討論和分析後,Play API團隊最終引入了中間數據加載器和數據存儲層,在服務和底層數據源之間有效地實現了一個物化視圖。

image

總而言之,與1型決策相關的數據架構建議是:

將數據與服務隔離。至少,確保數據源通過一個抽象層訪問,爲以後擴展留有餘地。

在這一部分的演講中,最後一條建議是說,在做2型決策時,應該“選擇一條路徑,實驗並迭代”。在構建服務時,決策的指導原則是重點識別所面臨的決策類型:

確定你的1型決策和2型決策;把80%的時間用於討論和調整1型決策。

演講的最後一部分重點探討了“總體”架構原則,並在開始時引用了Neal Ford、Rebecca Parsons和Patrick Kua所著的Building Evolutionary Architectures一書中的一句話;“在衆多維度中,演進式架構以支持指導式、增量式變更作爲首要原則”。Rangarajan認爲,前面討論的1型決策的結果最終產生了具有適當耦合的微服務架構,它爲所需的演化類型提供支持。他討論了使用“適應性函數”來監控和指導未來的變化,同時,他還討論了Play API團隊在設計架構時不可避免要做出的權衡,如簡單性勝過可靠性(例如回退會導致級聯問題),可伸縮性勝過吞吐量(如擴展高速緩存提高了性能,但由於最初的緩存預熱需要時間,所以沒有很好的擴展性)。

總之,Rangarajan表示,在一整年裏,新變更沒有導致生產事故,團隊接近平均每週部署4.5次的部署目標,而且只有兩次回滾。

InfoQ提供了演講“Netflix Play API:爲什麼我們建造一個演進式架構”的完整視頻和演講記錄。

查看英文原文:Netflix Play API: Building an Evolutionary Architecture

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章