Serverless/Faas/BaaS 等概念在這幾年的技術(shù)圈中是絕對的熱點(diǎn)詞匯之一,國內(nèi)外眾多云廠商也紛紛推出自家的 Serverless 和函數(shù)計(jì)算產(chǎn)品,微信也依托騰訊云推出了基于 Serverless 和 FaaS 理念的「小程序·云開發(fā)」,對應(yīng)的新型崗位也不斷涌現(xiàn)。
如今,我們確實(shí)看到很多中小型業(yè)務(wù)在拓展新領(lǐng)域、開發(fā)新功能時會優(yōu)先考慮使用 Serverless 產(chǎn)品作為后端架構(gòu)基建,但卻鮮有見到面向 C 端的一線大廠中,有核心業(yè)務(wù)在 Web 后端層面改為采用 Serverless 作為開發(fā)模式,這又是何緣由呢?
什么是Serverless
作為業(yè)界 Serverless 和 FaaS 領(lǐng)域的先鋒,Amazon 的 Serverless 產(chǎn)品頁面對 Serverless 作出了這樣的定義:
無服務(wù)器是一種用于描述服務(wù)、實(shí)踐和策略的方式,使您能夠構(gòu)建更敏捷的應(yīng)用程序,從而能夠更快地創(chuàng)新和響應(yīng)變化。憑借無服務(wù)器計(jì)算,容量預(yù)置和補(bǔ)丁等基礎(chǔ)設(shè)施管理任務(wù)由 AWS 處理,以便您能夠?qū)W⒂诰帉憺榭蛻舴?wù)的代碼。AWS Lambda 等無服務(wù)器服務(wù)具有自動擴(kuò)展、內(nèi)置高可用性以及按價值付費(fèi)的計(jì)費(fèi)模型。Lambda 是一種事件驅(qū)動的計(jì)算服務(wù),使您能夠運(yùn)行代碼來響應(yīng)來自 200 多個本地集成的 AWS 和 SaaS 源的事件 — 所有這些都無需管理任何服務(wù)器。
根據(jù) Amazon 的定義及各個廠商和業(yè)務(wù)對 Serverless 的實(shí)踐,我們可以進(jìn)一步將 Serverless 的特點(diǎn)歸納如下:
- 解耦開發(fā)與運(yùn)維:業(yè)務(wù)只需要關(guān)心自身的業(yè)務(wù)實(shí)現(xiàn),不需要關(guān)心機(jī)器情況,也無需進(jìn)行復(fù)雜的基礎(chǔ)組件配置甚至環(huán)境搭建。
- 彈性:借助于近幾年容器技術(shù)的發(fā)展,可實(shí)現(xiàn)毫秒級快速擴(kuò)容,并可根據(jù)流量彈性擴(kuò)縮容。
- 按需付費(fèi):借助彈性伸縮能力,使用者無需大量冗余機(jī)器,按需擴(kuò)縮容,根據(jù)請求量等指標(biāo)快速冷啟動,進(jìn)而按需付費(fèi)降低成本。
- 精細(xì)計(jì)費(fèi):理想情況下 Serverless 架構(gòu)中的每個原子應(yīng)用都應(yīng)該是基本無副作用的函數(shù),各函數(shù)可各自彈性擴(kuò)展,進(jìn)而實(shí)現(xiàn)函數(shù)級別的細(xì)粒度計(jì)費(fèi)。
極速擴(kuò)容并不極速
借助于容器技術(shù)的發(fā)展,Serverless 可做到毫秒級別的新容器彈出,如果是 Runtime 類型的實(shí)現(xiàn),函數(shù)初始化速度還會更快,云層面的「極速擴(kuò)容」是可行且實(shí)用的。
然而在應(yīng)用層面,雖然 Web 后端服務(wù)本身通常是無狀態(tài)的,但為了根據(jù)請求返回對應(yīng)的業(yè)務(wù)數(shù)據(jù),應(yīng)用還要與有狀態(tài)的 DB 、Redis 等持久層進(jìn)行交互,內(nèi)網(wǎng)的微服務(wù)間也需要相互連接以進(jìn)行通信,這通常都是基于 TCP 長連來進(jìn)行的。而由于創(chuàng)建連接本身就是一件很耗時的事情,再加上有的組件為了避免建連過程中的并發(fā)問題采用了串行建連,后端為了提升性能、避免服務(wù)因建連而導(dǎo)致的卡頓通常會在應(yīng)用初始化過程中、向外提供服務(wù)前就通過建立一批預(yù)留連接的方式來池化連接,再加上 Java 類應(yīng)用本身還有 JVM 即時編譯器需要逐步優(yōu)化字節(jié)碼導(dǎo)致的初啟動時性能差的問題,Web 后端應(yīng)用的啟動常常至少是分鐘級別的,在這樣的耗時比較下,毫秒級的容器或函數(shù)初始化能力只能說一定程度上優(yōu)化了應(yīng)用啟動的耗時,卻不能稱其為解決了整個鏈路的快速擴(kuò)容問題。
試想如果 C 端的 Web 后端服務(wù)完全依賴 Serverless 的彈性伸縮,不再根據(jù)預(yù)估的流量變化進(jìn)行機(jī)器冗余,甚至流量到來時才冷啟動函數(shù),那么在秒殺、其他系統(tǒng)雪崩帶來的流量大幅上漲的情況下,一旦出現(xiàn)擴(kuò)容速度慢于流量上漲速度的情況,服務(wù)將被瞬間打垮。
另一方面,按用量擴(kuò)容的邏輯通常會有并發(fā)度的限制,以便于擴(kuò)容時可做到盡量準(zhǔn)確進(jìn)而盡量貼近按需付費(fèi)的設(shè)計(jì)。但不同類型服務(wù)的監(jiān)控項(xiàng)和監(jiān)控閾值不盡相同,平臺很難不依賴業(yè)務(wù)壓測報告就能智能給出準(zhǔn)確結(jié)論。而在突發(fā)流量的場景下,按并發(fā)度擴(kuò)容受限于上文提到的應(yīng)用部署時間長的問題,很有可能出現(xiàn)擴(kuò)容速度追趕不上流量上漲速度的問題。
按需付費(fèi)難以實(shí)踐
結(jié)合極速擴(kuò)容在實(shí)際后端場景的局限性,中大型 Web 后端服務(wù)往往會更傾向于仍然按傳統(tǒng)的「人工評估流量峰值」、「壓測確定單機(jī)性能」、「按壓測結(jié)論提前手動擴(kuò)容進(jìn)行冗余」和「回歸壓測集群性能是否可滿足目標(biāo)」的流程來進(jìn)行流量運(yùn)維。但同時,后端通常對彈性擴(kuò)縮容接受度很高,因此會在手動擴(kuò)容的同時接入 Serverless 的彈性伸縮能力來保證極端流量發(fā)生時有擴(kuò)容的兜底手段。也就是說,很多場景下,后端仍要和過去一樣冗余機(jī)器來應(yīng)對流量,成本很難實(shí)質(zhì)性地被降低。而如果公司的基建是自建的云體系,那么彈性伸縮意味著需要預(yù)留機(jī)器資源池用于擴(kuò)容,所以整體的實(shí)際成本相較傳統(tǒng)方式可能還會有所上升。
中大型Web后端邏輯難以真正函數(shù)化
在 C 端業(yè)務(wù)發(fā)展的初期,從 MVP 到產(chǎn)品全量上線,后端邏輯大多面臨著兩天一小改、三天一大改的需求迭代狀態(tài),所以初期的代碼結(jié)構(gòu)、架構(gòu)設(shè)計(jì)都很難做到足夠合理化。而如果產(chǎn)品能存活到穩(wěn)定期,那么在沒有明確的性能、擴(kuò)展性問題的情況下,大部分團(tuán)隊(duì)都只會選擇以打補(bǔ)丁的方式進(jìn)行迭代而非大范圍重構(gòu)優(yōu)化。這樣一來,合理的函數(shù)化微服務(wù)設(shè)計(jì)對于存量服務(wù)來說也就很難落地。
那么如果在業(yè)務(wù)創(chuàng)建伊始就采用函數(shù)化的設(shè)計(jì)呢?我們知道 FaaS 在后端領(lǐng)域通常意味著更加精細(xì)的微服務(wù)拆分設(shè)計(jì)。在微服務(wù)大行其道的當(dāng)今,很多業(yè)務(wù)會采用 DDD 的思想進(jìn)行領(lǐng)域劃分進(jìn)而自然而然地形成自洽的微服務(wù)體系。但微服務(wù)間調(diào)用的耗時增加、可用率低于單體服務(wù)、調(diào)用關(guān)系需要配置復(fù)雜的熔斷降級策略等問題都是客觀存在的,微服務(wù)體系實(shí)際上是在性能和架構(gòu)合理性之間找尋平衡點(diǎn)。如果將微服務(wù)再進(jìn)一步拆分為更加細(xì)化的函數(shù),那么雖然架構(gòu)的合理性可能得以進(jìn)一步提升,但業(yè)務(wù)整體的性能和穩(wěn)定性問題的復(fù)雜程度將會有一個數(shù)量級的上升。
既然將微服務(wù)進(jìn)一步拆分為函數(shù)難以落地,那直接以原有的微服務(wù)作為函數(shù)呢?這樣確實(shí)可行,可是傳統(tǒng)架構(gòu)依賴容器技術(shù)的進(jìn)步已經(jīng)足以支撐此類業(yè)務(wù)的發(fā)展,Serverless 的優(yōu)勢將只剩下運(yùn)維層面的便捷,但上文中也講到所謂的運(yùn)維優(yōu)勢在 C 端場景中很難發(fā)揮實(shí)際作用,后端業(yè)務(wù)又有什么特別的理由必須選擇 Serverless 呢?
開發(fā)模式的巨大差異
對于 Web 后端常用的 Java 體系來說,Spring 已經(jīng)成為了既定的標(biāo)準(zhǔn),而目前的 FaaS 實(shí)現(xiàn)通常需要在代碼中配置觸發(fā)器消息的消費(fèi)者來實(shí)現(xiàn)功能定義,同時常常需要搭配 Runtime SDK 負(fù)責(zé)應(yīng)用啟動,這與傳統(tǒng)的 SpringMVC 等組件的開發(fā)思路差異較大,除了需要開發(fā)人員改變開發(fā)習(xí)慣、積累新的開發(fā)經(jīng)驗(yàn)外,存量的服務(wù)也將面臨著巨大的改造成本。
即使脫離語言的特定場景,不同云廠家提供的 SDK 也不盡相同,這也就意味著更換服務(wù)商需要修改代碼邏輯、重新測試回歸功能。而對于能靠自身能力提供一攬子 Serverless 方案的大廠,其 SDK 在可預(yù)見的未來不會有根本上的變化??梢坏┏鲇谛阅艿葐栴}的考慮,服務(wù)需要遷回 PaaS 類架構(gòu),或未來技術(shù)革新有了 FaaS 理念的替代者,那么工程師們所要面對的改造成本仍然十分巨大,如若處理不好,當(dāng)年對前沿技術(shù)的實(shí)踐就可能會變?yōu)槌林氐募夹g(shù)債。
Serverless真的無法應(yīng)用于傳統(tǒng)后端嗎?
對于 Serverless 的適用場景,阿里云的文檔給出了如下理解:
- 事件觸發(fā)的計(jì)算
- 實(shí)時視頻廣播的彈性調(diào)整大小
- 物聯(lián)網(wǎng)數(shù)據(jù)處理
- 共享交付調(diào)度系統(tǒng)
可見,目前商用的使用場景大多集中在對響應(yīng)耗時不敏感或完全異步的場景,那么傳統(tǒng)的 Web 后端項(xiàng)目真的就無法適應(yīng) Serverless 架構(gòu)了嗎?
相比起傳統(tǒng)的 PaaS 架構(gòu)主要針對的是運(yùn)維資源交付場景,F(xiàn)aaS 需要開發(fā)者顯式地參與到函數(shù)的編寫、配置和部署流程中,因此 FaaS 實(shí)際上是包含面向研發(fā)開放的理念的。既然如此,我們或許應(yīng)該減少對于 Serverless 運(yùn)維層面優(yōu)勢的討論,聚焦于開發(fā)層面的問題解決。
函數(shù)內(nèi)聯(lián)
目前的 FaaS 平臺中,函數(shù)定義大多與服務(wù)無異,函數(shù)間的調(diào)用仍然要通過網(wǎng)絡(luò)調(diào)用進(jìn)行。而如果我們將 FaaS 作為后端微服務(wù)的進(jìn)階狀態(tài),我們拆分的函數(shù)功能必然會更加干練,上下游依賴也會更加繁多,只將其作為獨(dú)立服務(wù)向外提供功能就意味著必然會造成耗時的上漲、穩(wěn)定性的降低,同時被加長了的調(diào)用鏈路也會提升維護(hù)難度。因此如果可以將云中的函數(shù)與代碼中的函數(shù)的定義打通,向基礎(chǔ)函數(shù)提供進(jìn)程內(nèi)通信方案,那么后端進(jìn)行函數(shù)化改造的顧慮將會低很多,可行性也會有質(zhì)的提升。
函數(shù)結(jié)構(gòu)化
在微服務(wù)的架構(gòu)下,每個服務(wù)的上下游依賴梳理向來都是件耗時耗力的工作。而如果將微服務(wù)拆分為更小的函數(shù),那么雖然我們解決了哪些邏輯該拆為微服務(wù)、拆多少個微服務(wù)的問題,但梳理子域中數(shù)量龐大的函數(shù)間的邏輯關(guān)系將會讓人更加望而卻步。
因此我們需要建立一套基于函數(shù)理念的結(jié)構(gòu)化視圖,函數(shù)在定義時可以與服務(wù)是多對多的關(guān)系,在運(yùn)行時函數(shù)實(shí)例與服務(wù)是多對一的關(guān)系,即函數(shù)組成服務(wù),服務(wù)向外提供統(tǒng)一業(yè)務(wù)能力。
另一方面,有了結(jié)構(gòu)化的函數(shù)視圖和管理能力后,我們也有了將函數(shù)進(jìn)行抽象公用、建立函數(shù)市場概念的可能,進(jìn)而便可以一定程度上解決重復(fù)開發(fā)的問題。
消除開發(fā)模式差異
對于不同廠家 SDK 差異大、相互不兼容的問題,Spring 給出了 Spring Cloud Function 這個答案。Spring Cloud Function 的設(shè)計(jì)有些類似于 ORM 框架的理念,提供統(tǒng)一的 API ,底層可根據(jù)配置對接不同的 FaaS 方案,這樣一來平臺遷移的成本就被降到了最低。同時,我們還應(yīng)考慮存量業(yè)務(wù)如何進(jìn)行函數(shù)化改造以及已有的 FaaS 項(xiàng)目如何回退到傳統(tǒng)架構(gòu)的問題,做到向前和向后兼容,進(jìn)而最大程度降低 FaaS 的接入成本,打消業(yè)務(wù)對 Serverless 架構(gòu)發(fā)展前景的顧慮。
此外,目前對于如遠(yuǎn)程調(diào)試、鏈路追蹤、日志統(tǒng)計(jì)等功能,平臺給出的解決方案與成熟的開發(fā)模式也有著較大差異,而對于 Java 項(xiàng)目來講 Runtime 類型的 FaaS 實(shí)現(xiàn)相當(dāng)于做了調(diào)試能力的功能閹割,因此平臺也需要迭代出與成熟開發(fā)模式相持平的能力,提高業(yè)務(wù)的研發(fā)和問題排查效率。
新老結(jié)合的伸縮方式
對于大流量的 C 端 Web 業(yè)務(wù)來說,完全依賴 Serverless 的彈性擴(kuò)容來根據(jù)調(diào)用進(jìn)行冷啟動是不現(xiàn)實(shí)的,因此平臺也需要支持傳統(tǒng)架構(gòu)的冗余部署策略,同時結(jié)合平臺已有的彈性伸縮能力來一定程度地降低業(yè)務(wù)冗余倍數(shù)進(jìn)而降低費(fèi)用,并以此提供突發(fā)流量的兜底擴(kuò)容方案。