看看頂級網際網路公司都在研究的無伺服器架構,看完收穫滿滿

2019-07-05   IT技術分享
來源:架構師社區


無伺服器計算(Severless computing,簡稱 Serverless)現在是軟體架構圈中的熱門話題,國外三大雲計算供應商(Amazon、Google 和 Microsoft)都在大力投入這個領域,湧現了不計其數的相關書籍、開源框架、商業產品、技術大會。到底什麼是 Serverless?它有什麼長處/短處?我希望通過本文對這些問題提供一些啟發。

一些示例

介面驅動的應用(UI-driven applications)

我們來設想一個傳統的三層 C/S 架構,例如一個常見的電子商務應用(比如在線寵物商店),假設它服務端用 Java,客戶端用 HTML/JavaScript:


在這個架構下客戶端通常沒什麼功能,系統中的大部分邏輯——身份驗證、頁面導航、搜索、交易——都在服務端實現。

把它改造成 Serverless 架構的話會是這樣:

這是張大幅簡化的架構圖,但還是有相當多變化之處:

  1. 我們移除了最初應用中的身份驗證邏輯,換用一個第三方的 BaaS 服務。
  2. 另一個 BaaS 示例:我們允許客戶端直接訪問一部分資料庫內容,這部分數據完全由第三方託管(如 AWS Dynamo),這裡我們會用一些安全配置來管理客戶端訪問相應數據的權限。
  3. 前面兩點已經隱含了非常重要的第三點:先前伺服器端的部分邏輯已經轉移到了客戶端,如保持用戶 Session、理解應用的 UX 結構(做頁面導航)、獲取數據並渲染出用戶介面等等。客戶端實際上已經在逐步演變為單頁應用。
  4. 還有一些任務需要保留在伺服器上,比如繁重的計算任務或者需要訪問大量數據的操作。這裡以「搜索」為例,搜索功能可以從持續運行的服務端中拆分出來,以 FaaS 的方式實現,從 API 網關(後文做詳細解釋)接收請求返迴響應。這個伺服器端函數可以和客戶端一樣,從同一個資料庫讀取產品數據。我們原始的伺服器端是用 Java 寫的,而 AWS Lambda(假定我們用的這家 FaaS 平台)也支持 Java,那麼原先的搜索代碼略作修改就能實現這個搜索函數。
  5. 最後我們還可以把「購買」功能改寫為另一個 FaaS 函數,出於安全考慮它需要在伺服器端,而非客戶端實現。它同樣經由 API 網關暴露給外部使用。

消息驅動的應用(Message-driven applications)

再舉一個後端數據處理服務的例子。假設你在做一個需要快速響應 UI 的用戶中心應用,同時你又想捕捉記錄所有的用戶行為。設想一個在線廣告系統,當用戶點擊了廣告你需要立刻跳轉到廣告目標,同時你還需要記錄這次點擊以便向廣告客戶收費(這個例子並非虛構,我的一位前同事最近就在做這項重構)。

傳統的架構會是這樣:「廣告伺服器」同步響應用戶的點擊,同時發送一條消息給「點擊處理應用」,異步地更新資料庫(例如從客戶的帳戶里扣款)。


在 Serverless 架構下會是這樣:


這裡兩個架構的差異比我們上一個例子要小很多。我們把一個長期保持在內存中待命的任務替換為託管在第三方平台上以事件驅動的 FaaS 函數。注意這個第三方平台提供了消息代理和 FaaS 執行環境,這兩個緊密相關的系統。

解構 「Function as a Service」

我們已經提到多次 FaaS 的概念,現在來挖掘下它究竟是什麼含義。先來看看 Amazon 的 Lambda 產品簡介:

通過 AWS Lambda,無需配置或管理伺服器(1)即可運行代碼。您只需按消耗的計算時間付費 – 代碼未運行時不產生費用。藉助 Lambda,您幾乎可以為任何類型的應用程式或後端服務(2)運行代碼,而且全部無需管理。只需上傳您的代碼,Lambda 會處理運行(3)和擴展高可用性(4)代碼所需的一切工作。您可以將您的代碼設置為自動從其他 AWS 服務(5)觸發,或者直接從任何 Web 或移動應用程式(6)調用。


  1. 本質上 FaaS 就是無需配置或管理你自己的伺服器系統或者伺服器應用即可運行後端代碼,其中第二項——伺服器應用——是個關鍵因素,使其區別於現今其他一些流行的架構趨勢如容器或者 PaaS(Platform as a Service)。回顧前面點擊處理的例子,FaaS 替換掉了點擊處理伺服器(可能跑在一台物理伺服器或者容器中,但絕對是一個獨立的應用程式),它不需要伺服器,也沒有一個應用程式在持續運行。
  2. FaaS 不需要代碼基於特定的庫或框架,從語言或環境的層面來看 FaaS 就是一個普通的應用程式。例如 AWS Lambda 支持 JavaScript、Python 以及任意 JVM 語言(Java、Clojure、Scala 等),並且你的 FaaS 函數還可以調用任何一起部署的程序,也就是說實際上你可以用任何能編譯為 Unix 程序的語言(稍後我們會講到 Apex)。FaaS 也有一些不容忽視的局限,尤其是牽涉到狀態和執行時長問題,這些我們稍後詳談。 再次回顧一下點擊處理的例子——代碼遷移到 FaaS 唯一需要修改的是 main 方法(啟動)的部分,刪掉即可,也許還會有一些上層消息處理的代碼(實現消息監聽介面),不過這很可能只是方法簽名上的小改動。所有其他代碼(比如那些訪問資料庫的)都可以原樣用在 FaaS 中。
  3. 既然我們沒有伺服器應用要執行,部署過程也和傳統的方式大相逕庭——把代碼上傳到 FaaS 平台,平台搞定所有其他事情。具體而言我們要做的就是上傳新版的代碼(zip 文件或者 jar 包)然後調用一個 API 來激活更新。
  4. 橫向擴展是完全自動化、彈性十足、由 FaaS 平台供應商管理的。如果你需要並行處理 100 個請求,不用做任何處理系統可以自然而然地支持。FaaS 的「運算容器」會在運行時按需啟動執行函數,飛快地完成並結束。 回到我們的點擊處理應用,假設某個好日子我們的客戶點擊廣告的數量有平日的十倍之多,我們的點擊處理應用能承載得住麼?我們寫的代碼是否支持並行處理?支持的話,一個運行實例能夠處理這麼多點擊量嗎?如果環境允許多進程執行我們能自動支持或者手動配置支持嗎?以 FaaS 實現你的代碼需要一開始就以並行執行為默認前提,但除此之外就沒有其他要求了,平台會完成所有的伸縮性需求。
  5. FaaS 中的函數通常都由平台指定的一些事件觸發。在 AWS 上有 S3(文件)更新、時間(定時任務)、消息總線(Kinesis)消息等,你的函數需要指定監聽某個事件源。在點擊處理器的例子中我們有個假設是已經採用了支持 FaaS 訂閱的消息代理,如果沒有的話這部分也需要一些代碼量。
  6. 大部分的 FaaS 平台都支持 HTTP 請求觸發函數執行,通常都是以某種 API 網關的形式實現(如 AWS API Gateway,Webtask)。我們在寵物商店的例子中就以此來實現搜索和購買功能。

狀態

當牽涉到本地(機器或者運行實例)狀態時 FaaS 有個不能忽視的限制。簡單點說就是你需要接受這麼一個預設:函數調用中創建的所有中間狀態或環境狀態都不會影響之後的任何一次調用。這裡的狀態包括了內存數據和本地磁碟存儲數據。從部署的角度換句話說就是 FaaS 函數都是無狀態的(Stateless)。

這對於應用架構有重大的影響,無獨有偶,「Twelve-Factor App」 的概念也有一模一樣的要求。

在此限制下的做法有多種,通常這個 FaaS 函數要麼是天然無狀態的——純函數式地處理輸入並且輸出,要麼使用資料庫、跨應用緩存(如 Redis)或者網絡文件系統(如 S3)來保存需要進一步處理的數據。

執行時長

FaaS 函數可以執行的時間通常都是受限的,目前 AWS Lambda 函數執行最長不能超過五分鐘,否則會被強行終止。

這意味著某些需要長時間執行的任務需要調整實現方法才能用於 FaaS 平台,例如你可能需要把一個原先長時間執行的任務拆分成多個協作的 FaaS 函數來執行。

啟動延遲

目前你的 FaaS 函數響應請求的時間會受到大量因素的影響,可能從 10 毫秒到 2 分鐘不等。這聽起來很糟糕,不過我們來看看具體的情況,以 AWS Lambda 為例。

如果你的函數是 JavaScript 或者 Python 的,並且代碼量不是很大(千行以內),執行的消耗通常在 10 到 100 毫秒以內,大函數可能偶爾會稍高一些。

如果你的函數實現在 JVM 上,會偶爾碰到 10 秒以上的 JVM 啟動時間,不過這只會在兩種情況下發生:

你的函數調用觸發比較稀少,兩次調用間隔超過 10 分鐘。

流量突發峰值,比如通常每秒處理 10 個請求的任務在 10 秒內飆升到每秒 100 個。

前一種情況可以用個 hack 來解決:每五分鐘 ping 一次給函數保持熱身。

這些問題嚴重麼?這要看你的應用類型和流量特徵。我先前的團隊有一個 Java 的異步消息處理 Lambda 應用每天處理數億條消息,他們就完全不擔心啟動延遲的問題。如果你要寫的是一個低延時的交易程序,目前而言肯定不會考慮 FaaS 架構,無論你是用什麼語言。

不論你是否認為你的應用會受此影響,都應該以生產環境級別的負載測試下實際性能情況。如果目前的情況還不能接受的話,可以幾個月後再看看,因為這也是現在的 FaaS 平台供應商們主要集中精力在解決的問題。

API 網關



我們前面還碰到過一個 FaaS 的概念:「API 網關」。API 網關是一個配置了路由的 HTTP 伺服器,每個路由對應一個 FaaS 函數,當 API 網關收到請求時它找到匹配請求的路由,調用相應的 FaaS 函數。通常 API 網關還會把請求參數轉換成 FaaS 函數的調用參數。最後 API 網關把 FaaS 函數執行的結果返回給請求來源。

AWS 有自己的一套 API 網關,其他平台也大同小異。

除了純粹的路由請求,API 網關還會負責身份認證、輸入參數校驗、響應代碼映射等,你可能已經敏銳地意識到這是否合理,如果你有這個考慮的話,我們待會兒就談。

另一個應用 API 網關加 FaaS 的場景是創建無伺服器的 http 前端微服務,同時又具備了 FaaS 函數的伸縮性、管理便利等優勢。

目前 API 網關的相關工具鏈還不成熟,儘管這是可行的但也要夠大膽才能用。

工具鏈

前面關於工具鏈還不成熟的說法是指大體上 FaaS 無伺服器架構平台的情況,也有例外,Auth0 Webtask 就很重視改善開發者體驗,Tomasz Janczuk 在最近一屆的 Serverless Conf 上做了精彩的展示。

無伺服器應用的監控和調試還是有點棘手,我們會在本文未來的更新中進一步探討這方面。

開源

無伺服器 FaaS 的一個主要好處就是只需要近乎透明的運行時啟動調度,所以這個領域不像 Docker 或者容器領域那麼依賴開源實現。未來肯定會有一些流行的 FaaS / API 網關平台實現可以跑在私有伺服器或者開發者工作站上,IBM 的 OpenWhisk 就是一個這樣的實現,不知道它是否能成為流行選擇,接下來的時間裡肯定會有更多競爭者出現。

除了運行時的平台實現,還是有不少開源工具用以輔助開發和部署的,例如 Serverless Framework 在 API 網關 + Lambda 的易用性上就比它的原創者 AWS 要好很多,這是一個 JS 為主的項目,如果你在寫一個 JS 網關應用一定要去了解下。

再如 Apex——「輕鬆創建、部署及管理 AWS Lambda 函數」。Apex 有意思的一點是它允許你用 AWS 平台並不直接支持的語言來實現 Lambda 函數,比如 Go。

什麼不是 Serverless

在前文中我定義了 「Serverless」 是兩個概念的組合:「Backend as a Service」 和 「Function as a Service」,並且對後者的特性做了詳細解釋。

在我們開始探討它的好處和弊端之前,我想再花點兒時間在它的定義上,或者說:區分開那些容易和 Serverless 混淆的概念。我看到一些人(包括我自己最近)對此都有困惑,我想值得對此做個澄清。


對比 PaaS

既然 Serverless FaaS 這麼像 12-Factor 應用,那不就是另一種形式的 Platform as a Service 麼?就像 Heroku?對此借用 Adrian Cockcroft 一句非常簡明的話:

如果你的 PaaS 能在 20ms 內啟動一個只運行半秒鐘的實例,它就叫 Serverless。— Adrian Cockcroft

換句話說,大部分 PaaS 應用不會為了每個請求都啟動並結束整個應用,而 FaaS 就是這麼做的。

好吧,然而假設我是個嫻熟的 12-Factor 應用開發者,寫代碼的方式還是沒有區別對麼?沒錯,但是你如何運維是有很大不同的。鑒於我們都是 DevOps 工程師我們會在開發階段就充分考慮運維,對吧?

FaaS 和 PaaS 在運維方面的關鍵區別是伸縮性(Scaling)。對於大多數 PaaS 平台而言你需要考慮如何伸縮,例如在 Heroku 上你要用到多少 Dyno 實例?對於 FaaS 應用這一步驟是完全透明的。即便你將 PaaS 配置為自動伸縮,也無法精細到單個請求級別,除非你有一個非常明確穩定的流量曲線可以針對性地配置。所以 FaaS 應用在成本方面要高效得多。

既然如此,何必還用 PaaS?有很多原因,最主要的因素應該是工具鏈成熟度。另外像Cloud Foundry 能夠給混合雲和私有雲的開發提供一致體驗,在寫就本文的時候 FaaS 還沒有這麼成熟的平台。

對比 NoOps

Serverless 並非「零運維」——儘管它可能是「無系統管理員」,也要看你在這個 Serverless 的道路走多深。

「運維」的意義遠不止系統管理,它還包括並不限於監控、部署、安全、網絡、支持、生產環境調試以及系統伸縮。這些事務同樣存在於 Serverless 應用中,你仍舊需要相應的方法處理它們。某些情況下 Serverless 的運維會更難一些,畢竟它還是個嶄新的技術。

系統管理的工作仍然要做,你只是把它外包給了 Serverless 環境。這既不能說壞也不能說好——我們外包了大量的內容,是好是壞要看具體情況。不論怎樣,某些時候這層抽象也會發生問題,就會需要一個來自某個地方的人類系統管理員來支持你的工作了。


對比存儲過程即服務

還有一種說法把 Serverless FaaS 看做「存儲過程即服務(Stored Procedures as a Service)」,我想原因是很多 FaaS 函數示範都用資料庫訪問作為例子。如果這就是它的主要用途,我想這個名字也不壞,但終究這只是 FaaS 的一種用例而已,這樣去考慮 FaaS 局限了它的能力。

我好奇 Serverless 會不會最終變成類似存儲過程那樣的東西,開始是個好主意,然後迅速演變成大規模技術債務。— Camille Fournier

但我們仍然值得考慮 FaaS 是否會導致跟存儲過程類似的問題,包括 Camille 提到的技術債。有很多存儲過程給我們的教訓可以放在 FaaS 場景下重新審視,存儲過程的問題在於:

  1. 通常依賴於服務商指定的語言,或者至少是指定的語言框架/擴展
  2. 因為必須在資料庫環境中執行所以很難測試
  3. 難以進行版本控制,或者作為應用包進行管理

儘管不是所有存儲過程的實現都有這些問題,但它們都是常會碰到的。我們看看是否適用於 FaaS:

第一條就目前看來顯然不是 FaaS 的煩惱,直接排除。

第二條,因為 FaaS 函數都是純粹的代碼,所以應該和其他任何代碼一樣容易測試。整合測試是另一個問題,我們稍後展開細說。

第三條,既然 FaaS 函數都是純粹的代碼,版本控制自然不成問題;最近大家開始關心的應用打包,相關工具鏈也在日趨成熟,比如 Amazon 的 Serverless Application Model(SAM)和前面提到的其他 Serverless 框架都提供了類似的功能。2018 年初 Amazon 還開放了 Serverless Application Repository(SAR)服務,方便組織分發應用程式和組件,也是基於 AWS Serverless 服務構建的。

需要的Java架構師方面的資料可以關注之後私信哈,回復「資料」領取免費架構視頻資料,記得要點贊轉發噢!!