一文詳解爲什麼Serverless比其他軟件開發方法更具優勢

本文定義並解釋了Serverless與其他應用程序架構的不同之處,然後“證明”了Serverless應用程序架構在實施得當的情況下會優於非Serverless架構。最後總結了很多經驗法則,幫助架構師和開發人員實現Serverless。

關鍵要點

  • Serverless應用程序不涉及操作服務器,並且應用程序的運行時間完全委託給了服務提供者。

  • Serviceful Serverless應用程序是一種儘可能利用第三方服務實現後端功能的應用程序。

  • Serviceful Serverless應用程序的主要優點是,與其他方式構建的應用程序相比,它們需要的後端代碼數量要少很多。

  • 更少的代碼意味着更少的技術債務,更好和更一致的持續軟件開發速度,併爲普通開發人員提供更好的可維護性。

  • 基礎設施即服務的興起引發了一種新的軟件開發最佳實踐(“雲原生”),Serverless也是如此。你不可能將雲原生應用程序遷移到功能即服務(FaaS)平臺,並期望它們會獲得最好的設計。

大多數關於Serverless應用程序的文章都沒有提供足夠的細節來解釋爲什麼Serverless比其他軟件開發方法更具優勢,或者在解釋爲什麼Serverless比非Serverless解決方案更好時顯得有點雜亂無章。本文解釋了Serverless與其他應用程序架構的不同之處,然後“證明”了Serverless應用程序架構在實施得當的情況下會優於非Serverless架構。最後總結了很多經驗法則,幫助架構師和開發人員實現Serverless。本文對作者在QCon NY 2018和Serverlessconf SF 2018大會上提出的概念和示例進行了擴展。

我們在優化什麼?

爲一般開發者考慮

如果說軟件正在蠶食世界,那麼企業的成功在越來越大的程度上依賴於自身構建和部署軟件的能力。由於大多數現代軟件通常都是作爲軟件即服務(SaaS)提供的,因此軟件開發是永久性的:企業需要隨着時間的推移增加軟件開發資源。因此,能夠長期推動良好軟件開發速度的企業應該勝過那些無法做到這一點的企業。

如果我們想要成爲成功的軟件開發企業,我們應該想辦法讓普通軟件開發人員能夠更容易地維護軟件。讓企業無法保持合理開發速度的主要痛點通常被描述爲“技術債務”,但其背後的原因通常與短期思維(寫代碼時不考慮可維護性)或對未來開發人員的能力假設有關(假設寫代碼的團隊會一直是維護代碼的團隊)。

代碼越少,複雜性越低

如果我們想要讓軟件變得長期可維護,需要遵循兩個基本策略:更少的定製代碼和更少的複雜性。通常,維護較少的代碼比維護較多的代碼會更容易。請注意,這裏是指定製代碼。使用文檔化程度較高且維護良好的庫或服務可以避免自己編寫和維護代碼所帶來的痛苦。

使軟件更易於維護的另一個策略是降低代碼庫的複雜性。或許,圈複雜度概念是衡量複雜性的最佳方式。想象一下,要爲最終用戶啓動和運行一個特定的路由,需要涉及系統管理、數據庫管理、網絡管理和IT運營。減少所需的組件數量就可以提高可維護性。

Serverless

今天,在開發新軟件時,通過更少的代碼和更低的複雜性來提高長期軟件可維護性的最佳方法是使用Serverless軟件架構。

定義

關於Serverless的定義存在很多爭議(比如“但仍然有服務器存在”、“它是個糟糕的名字”),但我認爲,當你理解了它的含義,就會知道這個名字其實是很有意義的:

  • 不涉及服務器操作

  • 我們不對運行時間負責

Serverless是我們過去25年來在SaaS中走的最後一步,因爲我們已經漸漸將越來越多的職責交給了服務提供商。我們已經從自有數據中心轉向了主機託管設施,從專用託管到基礎設施即服務,從虛擬機到容器,但是它們都需要我們做一些個性化的系統工作。最初,我們遇到了很多問題,即使使用容了器,仍然需要打修補和處理第三方應用程序(例如Web服務器)故障。

Serverless將個性化服務器操作的數量降低到零,因爲一切都是多租戶的,並由服務提供商負責提供(注意:根據我的定義,在自己的Kubernetes集羣上運行功能並不是“Serverless”)。在Serverless架構中,你不需要處理硬件設置、成本支出、操作系統安裝和修補、應用程序安裝和修補,或第三方應用程序故障。

“不操作服務器”的必然結果是“我們不對運行時間負責”。運行時間——除非是你自己的代碼出了問題——取決於服務提供商。對於很多人來說,這是很可怕的(也許是因爲他們不相信服務提供商,也許是因爲他們擔心他們會因此丟掉飯碗),但如果你的目標是減少代碼和複雜性,只要服務提供商是可靠的,那麼這就是實現目標的最佳方式。

Serverless的好處

並非所有Serverless架構都是一樣的,有時候,Serverless應用程序有可能比典型的三層式單體應用程序更難維護。但是,如果實施得當,Serverless應用程序將提供比其他無法不使用Serverless的應用程序架構更大的優勢:更少的相互依賴、更少的技術債務、一種有效的微服務架構,以及獨立的生產等效環境。

恰當的Serverless架構存在較少的相互依賴性,因爲整體應用程序的複雜性較低。Serverless應用程序包括應用程序前端代碼、後端功能、第三方服務(例如Twilio、Algolia),以及這些功能和服務的配置。Serverless應用程序不處理操作系統、第三方服務器應用程序(如Web服務器)或任何特定於單個虛擬機或容器的部署代碼。因此,相對於基於虛擬機或容器應用程序的開發者,Serverless應用程序開發者被迫等待另一個團隊或其他團隊成員的人數會更少。

在所有的應用程序架構中,Serverless應用程序擁有的代碼量最少。我們將在下一節中通過一個示例來演示如何通過利用最有效的服務來消除代碼。

儘管Martin Fowler建議在嘗試將應用程序分解爲微服務時先從單體架構開始,但隨着我們轉向具有自動可擴展後端功能的胖客戶端,同時也獲得了可靠的微服務架構。如果我們的應用程序需要擴展給越來越多的用戶使用,我們在擴展應用程序方面的麻煩就會少很多,因爲我們已經將前端與功能分開,並且功能之間也是相互分離的。

最後,由於Serverless應用程序是按照使用計費的,免除了空閒成本,因此,相比在虛擬機或容器架構中爲所有開發人員提供單個開發環境,爲每個開發人員提供單獨且與生產環境等效的開發環境將更加便宜。這有助於提升開發人員的開發速度(如果有人搞壞自己的環境並不會阻礙到其他人)、應用程序部署代碼(如果可能,自動部署所有人的代碼),以及減少因環境差異造成的bug。

一個例子

讓我們來看一個特定的應用程序示例,Hacker News上的“keepingscore”認爲這是一個不能也不應該通過Serverless方式構建的應用程序:

“文章的作者認爲後端只是一個簡單的帶有身份驗證的持久層。我是一家旅遊公司的後端團隊負責人。我們需要在Android應用程序上呈現航班和酒店列表,但不是通過查找數據庫的方式來實現的,而是通過數百個SOAP和REST調用將來自多個平臺的航空公司和酒店數據拼接在一起。這個是前端無法處理的。即使可以,我也不想在iOS、Android、Web和我們的內部支持門戶之間複製這種邏輯。”

應用程序

基於上面的描述,我們假設有一個由一個網站和兩個移動應用程序前端(Android和iOS)組成的應用程序,它們與提供用戶管理的後端發生交互,用戶能夠搜索、購買和分享航班和酒店行程,還提供了一個內部會計所必需的報告層。

更具體地說,我們可能需要考慮以下一些高級功能:

  • 用戶管理:

    • 註冊、登入登出、修改密碼;
    • 保存用戶首選項。
  • 用戶界面:搜索和購買:

    • 自動填充搜索條件(例如機場代碼);
    • 提交搜索條件;
  • 支付。

  • 用戶界面:分享和修改:

    • 查看,與其他用戶分享行程;
    • 更改(包括取消)行程。
  • 後端:搜索和購買:

    • 並行調用多個API;
    • 合併響應,應用過濾器;
    • 購買行程。
  • 後端:報告:

    • 運行有關購買、行程的分析報告。

現在,我們將介紹不同的架構選項,說明Serverless架構能夠提供其他架構無法提供的優勢。請注意,我們不需要太過關注客戶端,因爲我們可以假設所有客戶端代碼在這些不同的架構中都是相同的,唯一不同的是後端是如何實現的(另請注意,最終用戶並不關心後端如何實現)。

架構1:典型的三層架構

image

在典型的現代三層架構中,應用程序服務器需要使用一個帶有緩存層的關係數據庫。從服務器運維角度來看,即使使用了容器,仍然需要處理的一些問題:配置和修補Web服務器和數據庫軟件、加速鏡像、預熱鏡像,以及容器或虛擬機故障轉移。如果使用虛擬機或自己的機器,需要做的事情會更多。

此外,驅動前端所需的代碼通常都是定製的。我通過查看各種示例應用程序來估計代碼行數,並估計後端的代碼行數如下:

image

架構2:純粹的FaaS

image

亞馬遜在2014年推出Lambda時,Serverless應用程序架構看起來很像上面那樣,基本上將我們架構1中的單體應用程序分解爲一系列功能(微服務),它們相互調用並與持久層交互,以此來響應客戶端的請求。但是,我不認爲這種架構是對三層架構的重大改進,雖然它確實有一些好處,但它也存在潛在的複雜性。

從好的方面來看,這種架構消除了三層架構所需的服務器操作。它是一種真正的Serverless架構。

不過,部署所有這些微服務並讓它們之間相互調用可能會出現大問題。功能即服務(FaaS)並不像應用程序的功能那樣,它們具有更高的調用開銷,並且如果出現無限循環將耗費大量成本(因爲每次調用都是收費的)。調試FaaS嵌套也比在調試器中單步調試應用程序要困難得多。最後,在這個示例中,你並沒有真正消除很多代碼,因此驅動應用程序可維護性的核心要求並不在於此。

image

架構3:Serviceful Serverless

image
這是一種更好的Serverless架構,我稱之爲Serviceful Serverless應用程序。在該架構中,不一定是唯一或不一定與標準功能(例如用戶管理和認證)不同的應用程序的各個部分由託管服務(例如,AWS Cognito、Auth0、Google Firebase Auth)來處理。

image

等等,這怎麼可能?

一些從業者對上表的第一反應是懷疑,他們不相信可以減少這麼多代碼,因爲如果可以這樣的話,那麼這種架構的靈活和可擴展程度足以在新需求出現時持續開發新功能。在本文中,我只能盡我最大努力來說服你,這些架構確實提供了可擴展、極低代碼量的應用程序,你最好通過嘗試的方式來了解它的好處。

有一些東西可以幫助人們從高層次理解如何實現代碼行數的減少和可擴展性的提升。首先,可以先看看示例應用程序是怎麼樣運行的。AWS已經構建了一個組日曆應用程序(截至2018年11月),Web版本(React)包含了702行JavaScript代碼,原生Android版本包含了508行Java代碼。如果你剛好在想着如何入門,AWS Amplify和Google Firebase提供的選項可讓你在不到一個小時內啓動並運行這種應用程序。

其次,Serviceful Serverless應用程序的一個關鍵創新是,以前需要通過代碼完成的很多事情現在可以在服務配置中完成——根據服務的不同,有時候使用了圖靈不完備的語言。圖靈不完備的語言非常適合用作配置,並且最終會減少很多代碼。

最後,你始終可以選擇按照以前的方式完成工作,在必要時只需要編寫自己的代碼,而不是利用現有的服務。換句話說,Serviceful Serverless架構中最糟糕的情況是,你可以按照以前的方式來完成應用程序的某些部分。但對於應用程序的其他部分,你可以將絕大多數工作交給不需要你構建或維護的服務。

有關Serviceful Serverless的更多詳細信息

Serviceful Serverless的一個關鍵部分是有一個託管服務處理來自客戶端的API調用,並根據需要將它們路由到各種服務和功能,而不是爲後端功能提供一個非常輕量的“網關”。這類服務的最佳例子可能是AWS AppSync,儘管Google Firebase和Hasura也提供了API Hub服務(請注意,這些服務不同於現在的FaaS服務,因此在選擇它們之前請先進行了解和測試!)。

這些API Hub服務的一個巨大好處是你可以通過它們直接將大多數請求路由到持久層,也可以將請求從持久層路由出去,而無需定製代碼。在上面以FaaS爲中心的架構和三層應用程序中,每個數據庫讀取和寫入都需要通過你開發的應用程序代碼。在上述的Serverless架構中,API Hub可以處理其中的很多請求——包括細粒度訪問控制(請參閱https://docs.aws.amazon.com/appsync/latest/devguide/security.html#amazon-cognito-user-pools-authorization和https://firebase.google.com/docs/database/security/user-security)。

通過爲所有後端功能使用託管服務,以及利用API Hub處理數據存儲的讀寫請求,定製代碼的行數急劇下降,並實現了應用程序的可維護性,而這是其他架構無法實現的。

反駁論點

與使用其他替代方案一樣,你總是無法避免風險。從典型的三層應用程序架構(或多個雙層微服務)遷移到Serviceful Serverless架構有助於提升應用程序的可維護性,但存在一些新的風險。我相信,在絕大多數情況下,這些風險是一種超過可接受的權衡,但我不想忽視它們。

我們不對運行時間負責

在我對Serverless的定義中,我將正常運行時間稱爲不是我們能夠控制的東西。數據顯示,主要的IaaS提供商在保持運行時間方面遠遠優於一般(甚至高於平均水平)的IT運營團隊,因此,爲他們支付遠低於內部運營團隊的費用來保持Serverless應用程序的運行(包括處理自動故障轉移)對我來說完全不是個事。

也就是說,如果你構建一個依賴於很多不同服務提供商的應用程序,並儘可能接近100%的運行時間,隨着服務提供商數量的增加,應用程序在未來某個時候發生故障的可能性也會增加。通常情況是提供商的數量越少越少。此外,明智的做法是提升應用程序處理非必要服務可用性的彈性。

供應商鎖定

如果沒有人喋喋不休地關注供應商鎖定,服務的差異化就無從談起。當然,有一些殘酷的案例表明,IT供應商利用企業對他們的依賴,將產品的價值榨取到無以復加的地步。但是,如果你擔心供應商鎖定,至少應該要說服自己,擺脫特定供應商鎖定並沒有那麼困難。

對於上面的Serviceful Serverless應用程序架構,你可以通過加入之前被免掉的定製代碼遷移出這種架構。也就是說,遷移成本從11,000行代碼變成了36,500行代碼。如果你認爲編寫25,000行代碼代價非常高(確實是這樣,因爲你必須維護所有新代碼!),那麼你支付給供應商的費用或許是合理的。但如果你認爲編寫代碼成本更低,那麼你就可以編寫代碼。此外,編寫代碼並不困難,因爲他們已經提供了很好的API文檔,並且你已經擁有可以調用這些API的代碼。

換句話說,通過使用Serviceful Serverless架構,你可以避免自己編寫代碼,因爲有另一個團隊在爲你編寫代碼,你只需要付給他們費用。如果你認爲費用太高,可以在內部使用它們,並自行開發代碼。這與數據庫或虛擬化層的鎖定不同,無狀態應用程序代碼更容易遷移。

經驗法則

採用新架構概念最困難的地方在於瞭解你的實現方式對不對。我有三個“經驗法則”可供你參考,用來判斷應用程序是否正確利用了Serviceful Serverless的優勢。

厚客戶端,不是厚中間層

你希望將通用邏輯(如上例中的酒店/航班搜索)放在後端,並打算將十年前開發的應用程序中間層的很多內容轉到服務(例如用於圖像處理的Cloudinary或用於搜索的Algolia)或胖客戶端(例如JavaScript框架)中。絕大多數定製代碼應該放在客戶端交互層中。這並不意味着你就不會有後端代碼(例如上面示例中提到的API集成),能夠控制所有最終用戶交互的細節通常來說是有價值的,但自己開發可在其他地方購買的後端功能幾乎是沒有價值的。

功能是粘合劑,不要相互調用

如上所述,在Serverless架構內,功能之間的互相調用是一個錯誤的做法。從調試的難度到調用的開銷,再到無限循環的成本,如果你能夠消除造成圈複雜度的原因,那說明你很有錢。相反,你的定製代碼功能應該是服務之間的粘合劑,例如接受客戶端請求,從各種API中提取數據,將其合併爲緊湊的數據結構,並將它們發送給數據存儲和客戶端。

自定義研究,而非自定義代碼

如果你正在構建Serviceful Serverless應用程序,那麼你需要花費更多的時間進行研究。這是因爲你將基於服務實現更多的應用程序功能,並且需要驗證選擇了正確的服務。你還需要找出將服務與應用程序集成的正確方法。因此,你可以考慮花幾天甚至幾周時間編寫概念驗證代碼,並測試不同的選項,而不是花一兩個小時尋找你可能會用到的軟件包。

換一種說法(兩個方程式):

2周研究 + 1天開發 → N行代碼需要維護

1天研究 + 2周開發 → 10N行代碼需要維護

十倍代碼行數就是十倍的技術債務,這意味着未來開發速度會越來越慢,越不可預測,普通開發人員也無法很好地維護系統。

關於作者

image
Joe Emison是一位連續技術聯合創始人,在3月份創辦了他的第五家公司Branch。他之前的項目包括BuildFax(被DMGT收購)、Spaceful(被Xceligent收購)、BluePrince(被Harris Computer收購)和EphPod(被Wind Solutions收購)。此外,他還爲其他公司提供軟件開發和雲遷移方面的諮詢,其中包括很多DMGT產品組合。Joe畢業於威廉姆斯學院,獲得英語和數學學位,並擁有耶魯大學法學院的法律學位。

查看英文原文:
https://www.infoq.com/articles/serverless-sea-change

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