如何落地一個FaaS平臺?
函數(shù)即服務(wù)(FaaS)作為云計(jì)算 2.0 時代重要的發(fā)展方向,能夠從工程效率、可靠性、性能、成本等方面給開發(fā)者帶來巨大的價(jià)值,尤其是能夠極大地提升研發(fā)效率。因此,擁抱FaaS成為開發(fā)者關(guān)心的重要技術(shù)領(lǐng)域。阿里文娛技術(shù)專家墨洵、研發(fā)工程師武升將介紹文娛函數(shù)計(jì)算平臺的設(shè)計(jì)思想與關(guān)鍵技術(shù)難點(diǎn),并結(jié)合業(yè)務(wù)介紹函數(shù)計(jì)算的落地實(shí)踐經(jīng)驗(yàn)。
一 背景
優(yōu)酷內(nèi)容分發(fā)業(yè)務(wù)涵蓋了優(yōu)酷主客的首頁、頻道頁、二級頁等不同場景下的內(nèi)容分發(fā),服務(wù)端之前采用傳統(tǒng)的Java應(yīng)用結(jié)合阿里集團(tuán)中間件的開發(fā)模式,一直是產(chǎn)品評審、API設(shè)計(jì)、前后端聯(lián)調(diào)、前后端發(fā)版等節(jié)奏。然而,隨著端上內(nèi)容的多樣化,產(chǎn)品需求迭代的加速,傳統(tǒng)的服務(wù)端架構(gòu)開發(fā)模式已顯得力不從心,我們雖然沉淀出一套通用框架,但受限于開發(fā)模式的本質(zhì)并沒有變化,業(yè)務(wù)開發(fā)的靈活性與開發(fā)成本依然很高??偨Y(jié)起來,面臨的挑戰(zhàn)主要是:API依賴數(shù)據(jù)源多,業(yè)務(wù)需求變化快,前后端聯(lián)調(diào)成本大等。
隨著Serverless技術(shù)的發(fā)展,F(xiàn)aaS的相關(guān)實(shí)踐探索都在阿里內(nèi)部逐漸多起來,我們思考了FaaS的特點(diǎn)和面臨的挑戰(zhàn),希望通過FaaS技術(shù)的引入,把一系列基礎(chǔ)能力沉淀下來,在此之上,通過FaaS來承接上層業(yè)務(wù)邏輯,阿里巴巴文娛優(yōu)酷FaaS平臺應(yīng)運(yùn)而生。
二 平臺設(shè)計(jì)與技術(shù)難點(diǎn)
1 設(shè)計(jì)目標(biāo)
希望實(shí)現(xiàn)一個通用的函數(shù)計(jì)算平臺,在這個平臺上,開發(fā)者直接通過編寫、運(yùn)行和管理一個或多個函數(shù)對外提供服務(wù),允許通過微服務(wù)、HTTP接口、事件源觸發(fā)等多種方式調(diào)用函數(shù)。同時,函數(shù)的開發(fā)及發(fā)布應(yīng)該是秒級生效,且無需重啟宿主應(yīng)用的,這樣就可以克服傳統(tǒng)Java應(yīng)用發(fā)布部署的時間成本,極大的減輕開發(fā)者在代碼開發(fā)之外的時間成本,同時可以快速回滾。
FaaS平臺應(yīng)該提供函數(shù)式應(yīng)用的運(yùn)行環(huán)境,應(yīng)該支持輕量級腳本語言編寫函數(shù)。我們首選Groovy語言,主要是考慮了Groovy的代碼簡潔,同時可以訪問Java的原生的類和對象。
FaaS可以根據(jù)實(shí)際的訪問情況進(jìn)行函數(shù)實(shí)例的動態(tài)加載和資源分配。
總結(jié)起來,在FaaS平臺上運(yùn)行的函數(shù)應(yīng)該是一個短小、離散、可復(fù)用的代碼塊,我們希望它有以下幾個特點(diǎn):
- 生命周期短,支持快速發(fā)布部署
- 非守護(hù)進(jìn)程(不需要長時間運(yùn)行,按需加載)
- 不提供長連接服務(wù)
- 無狀態(tài)
- 可重用現(xiàn)有服務(wù)或第三方資源(重點(diǎn),F(xiàn)aaS應(yīng)該建立在完善的基礎(chǔ)服務(wù)上)
- 毫秒級執(zhí)行時間
2 平臺整體設(shè)計(jì)
FaaS平臺的整體核心架構(gòu)主要由網(wǎng)關(guān)、運(yùn)行時容器、一站式運(yùn)維發(fā)布平臺、基礎(chǔ)服務(wù)等組成:
網(wǎng)關(guān)層主要負(fù)責(zé)接受函數(shù)調(diào)用請求,通過函數(shù)的唯一標(biāo)識及函數(shù)的集群信息分發(fā)函數(shù)調(diào)用到對應(yīng)集群的機(jī)器環(huán)境中執(zhí)行。
函數(shù)容器層是整個系統(tǒng)的核心,主要通過函數(shù)執(zhí)行引擎進(jìn)行實(shí)例的調(diào)用執(zhí)行,同時負(fù)責(zé)函數(shù)實(shí)例的生命周期管理,包括按需加載、代碼預(yù)熱、實(shí)例卸載回收等工作。
一站式發(fā)布運(yùn)維平臺(FaaS Platform)是面向開發(fā)者的主要操作平臺,開發(fā)者在平臺上進(jìn)行函數(shù)編寫、版本提交發(fā)布、回滾、監(jiān)控運(yùn)維等一系列工作。整個監(jiān)控體系打通了集團(tuán)的基礎(chǔ)服務(wù)監(jiān)控體系,,可以提供實(shí)時大盤,集群性能等基本監(jiān)控指標(biāo)的查詢功能。
整個FaaS平臺建立在集團(tuán)中間件以及優(yōu)酷內(nèi)容分發(fā)依賴的各基礎(chǔ)服務(wù)之上,通過良好的封裝向開發(fā)者提供簡潔的服務(wù)調(diào)用方式,同時函數(shù)本身的執(zhí)行都是運(yùn)行在互相隔離的環(huán)境中,通過統(tǒng)一的函數(shù)實(shí)例管理,進(jìn)行函數(shù)的調(diào)度、執(zhí)行監(jiān)控、動態(tài)管理等。
整體技術(shù)棧服務(wù)端容器層主要是采用Java實(shí)現(xiàn),結(jié)合集團(tuán)中間件完成整個容器層的主要功能。
前端主要基于React框架和Dva狀態(tài)管理框架實(shí)現(xiàn)。當(dāng)然,在實(shí)際開發(fā)過程中我們選擇了螞蟻金服的Bigfish框架和Odin腳手架。React提供了組件化的概念,這意味著我們開發(fā)的組件可以像HTML基本DOM元素一樣不斷被復(fù)用。為了實(shí)現(xiàn)組件的復(fù)用化和研發(fā)效率的提升,Bigfish在Web頁面上進(jìn)行了分層設(shè)計(jì),細(xì)粒度從大到小依次為:頁面模板 -> 區(qū)塊 -> 業(yè)務(wù)組件 -> 組件。Odin腳手架是優(yōu)酷推出一款面向中后臺業(yè)務(wù)系統(tǒng)的前端開發(fā)腳手架,集成了Bigfish的框架,支持以配置化的方式構(gòu)建網(wǎng)站路由,使得開發(fā)者不需要關(guān)注過多底層細(xì)節(jié),可以快速上手實(shí)現(xiàn)業(yè)務(wù)邏輯和頁面構(gòu)建。
類似于服務(wù)端側(cè)的MVC分層模式,前端在實(shí)現(xiàn)業(yè)務(wù)邏輯和數(shù)據(jù)通信時也有對應(yīng)的封層設(shè)計(jì)模式,來實(shí)現(xiàn)組件的狀態(tài)管理。經(jīng)歷了從Flux -> Redux -> Dva的衍變,狀態(tài)管理機(jī)制對復(fù)雜業(yè)務(wù)帶來的益處正在不變突出。Dva的完整數(shù)據(jù)流圖如下:
State是負(fù)責(zé)保存整個應(yīng)用狀態(tài),View是React組件構(gòu)成的視圖層,Action是描述事件的對象。connect方法是綁定 State 到 View的函數(shù),使得View層的組件可以動態(tài)監(jiān)聽State中的屬性,同時可以通過dispatch方法負(fù)責(zé)將Action發(fā)送至State觸發(fā)狀態(tài)改變。觸發(fā)狀態(tài)改變有兩種類型的函數(shù):effect函數(shù)和reducer函數(shù)。前者會與服務(wù)端進(jìn)行數(shù)據(jù)通信,可以處理異步動作;后者處理同步動作,并直接更新State。
FaaS Platform前端主要分為函數(shù)創(chuàng)建、函數(shù)管理、函數(shù)發(fā)布、函數(shù)模板和應(yīng)用統(tǒng)計(jì)五個模塊。在FaaS Platform系統(tǒng)中,函數(shù)是對外可被調(diào)度的最小單元,而應(yīng)用是劃分機(jī)器資源的最小單位,所以我們設(shè)定應(yīng)用與函數(shù)存在一對多的映射關(guān)系。
函數(shù)創(chuàng)建模塊
函數(shù)創(chuàng)建模塊主要提供添加函數(shù)的功能。一個完整函數(shù)必須包括函數(shù)名稱、函數(shù)標(biāo)識、函數(shù)類型、函數(shù)所屬應(yīng)用及應(yīng)用下所屬分類等基本信息;同時類似于mtop網(wǎng)關(guān),我們提供對于函數(shù)入?yún)?、響?yīng)業(yè)務(wù)結(jié)果、響應(yīng)業(yè)務(wù)錯誤碼的配置頁面,用于自動生成函數(shù)調(diào)用入?yún)⒈韱魏秃瘮?shù)接口文檔。函數(shù)的英文標(biāo)識唯一確定一個函數(shù),不可重復(fù)。
函數(shù)管理模塊
函數(shù)管理模塊主要提供函數(shù)的CRUD操作和函數(shù)的在線編寫功能。在本頁面我們可以快速進(jìn)行復(fù)雜條件的函數(shù)查詢和函數(shù)基本信息和狀態(tài)的編輯。同時我們提供函數(shù)編寫的在線Web IDE,支持文件增刪、代碼編寫、自動保存、函數(shù)提交、函數(shù)調(diào)試、日志打印等功能。
函數(shù)發(fā)布模塊
函數(shù)發(fā)布模塊主要提供函數(shù)提交歷史的查詢和執(zhí)行函數(shù)發(fā)布的功能。我們像傳統(tǒng)Java應(yīng)用支持引入二三方依賴,但不同于傳統(tǒng)的Java應(yīng)用發(fā)布,F(xiàn)aaS Platform系統(tǒng)中的函數(shù)發(fā)布可以實(shí)現(xiàn)秒級發(fā)布。目前函數(shù)發(fā)布已經(jīng)支持函數(shù)回滾發(fā)布和函數(shù)分批次發(fā)布,從部署環(huán)節(jié)實(shí)現(xiàn)對復(fù)雜多變業(yè)務(wù)需求的快速響應(yīng)。
函數(shù)模板模塊
函數(shù)模板模塊主要提供函數(shù)模板的CRUD操作和函數(shù)的在線編寫功能。結(jié)合實(shí)際的業(yè)務(wù)場景,我們首先提供一些基礎(chǔ)的內(nèi)置模板,方便函數(shù)的快速初始化。同時對于某一個業(yè)務(wù)問題的完整解決方案,我們允許該函數(shù)保存為自定義的函數(shù)模板。函數(shù)模板的Web IDE同樣支持函數(shù)模板的在線編寫、調(diào)試、自動保存等功能。
應(yīng)用統(tǒng)計(jì)模塊
由于函數(shù)隸屬于應(yīng)用從而具備機(jī)器資源,我們計(jì)劃提供應(yīng)用統(tǒng)計(jì)模塊以應(yīng)用為拆分進(jìn)行函數(shù)上線狀態(tài)、發(fā)布版本的數(shù)據(jù)統(tǒng)計(jì);同時我們也基于函數(shù)日志提供函數(shù)調(diào)用情況(調(diào)用量、成功率、響應(yīng)時間)的統(tǒng)計(jì)分析和監(jiān)控。關(guān)于具備的細(xì)節(jié),我們正在逐步實(shí)現(xiàn)和完善。
3 主要特性
優(yōu)酷FaaS平臺的主要特性是開發(fā)接入低成本、函數(shù)運(yùn)行時環(huán)境隔離以及運(yùn)維監(jiān)控操作的透明化。
開發(fā)接入低成本
FaaS平臺通過一站式的云端開發(fā)平臺,使用戶可以直接面向業(yè)務(wù)邏輯的開發(fā),而無需關(guān)注基礎(chǔ)服務(wù)及中間件的依賴,平臺本身提供完善的基礎(chǔ)能力封裝,包括:快捷開發(fā)能力,中間件快速接入能力,數(shù)據(jù)存儲快速接入能力,基礎(chǔ)能力封裝直接調(diào)用等。
業(yè)務(wù)邏輯開發(fā)模式輕量化、無應(yīng)用化,發(fā)布回滾秒級生效,極大的減輕了傳統(tǒng)服務(wù)端開發(fā)過程的繁瑣流程,將開發(fā)者的精力更多的集中于核心業(yè)務(wù)邏輯的開發(fā)。
同時提供如下的簡潔易于操作的開發(fā)部署流程設(shè)計(jì),減輕開發(fā)者開發(fā)部署的時間成本。
FaaS平臺上的函數(shù)除了開發(fā)成本低,調(diào)用者接入的方式也比較簡單。我們同時提供了中心化和去中心化兩種使用方式,不管去中心化還是中心化使用方式,函數(shù)代碼的編寫、調(diào)試、發(fā)布均在一站式運(yùn)維發(fā)布平臺上完成。在中心化接入方式下,我們通過統(tǒng)一的函數(shù)服務(wù)集群提供對外服務(wù),允許調(diào)用者通過統(tǒng)一的函數(shù)調(diào)用接口以HSF服務(wù)或者HTTP接口調(diào)用函數(shù),而函數(shù)代碼的執(zhí)行完全在我們的函數(shù)服務(wù)集群上,開發(fā)者無需自己申請應(yīng)用。
對于去中心化接入方式,開發(fā)者如果想調(diào)用函數(shù)平臺上的FaaS函數(shù),可以引入我們提供的SDK,此時,函數(shù)的執(zhí)行完全在調(diào)用者應(yīng)用的本地進(jìn)程里,F(xiàn)aaS平臺只提供函數(shù)的開發(fā)發(fā)布功能。
運(yùn)行時環(huán)境的隔離
運(yùn)行時環(huán)境的隔離分為兩個層次,一個層次是函數(shù)容器內(nèi)部函數(shù)實(shí)例之間的隔離;另外一個層次是不同函數(shù)本身就運(yùn)行在不同的虛擬應(yīng)用集群上,集群與集群之間的隔離性。
函數(shù)容器內(nèi)部函數(shù)實(shí)例的隔離指的是在FaaS平臺上編寫的Groovy函數(shù)運(yùn)行在統(tǒng)一的JVM進(jìn)程中,每個函數(shù)在開發(fā)的過程中都會生成多個版本,而不同函數(shù)之間、同一函數(shù)的不同版本之間在運(yùn)行時的環(huán)境都是相互隔離,互不干擾的。
函數(shù)運(yùn)行集群的隔離性主要是根據(jù)函數(shù)的訪問量、函數(shù)的服務(wù)特點(diǎn)(長尾服務(wù)還是通用服務(wù))等特性,在函數(shù)創(chuàng)建之初就將函數(shù)綁定在不同的虛擬應(yīng)用上,而不同的應(yīng)用會運(yùn)行在不同的機(jī)器集群上,函數(shù)在被調(diào)用時,網(wǎng)關(guān)層可以根據(jù)函數(shù)的應(yīng)用將函數(shù)的調(diào)用分發(fā)到不同的集群上執(zhí)行,保證函數(shù)之間物理隔離。
運(yùn)維監(jiān)控的透明化
FaaS平臺的函數(shù)都能在平臺上直接進(jìn)行監(jiān)控運(yùn)維操作,我們通過在函數(shù)執(zhí)行流程上收集函數(shù)的執(zhí)行日志,并將日志實(shí)時上報(bào)到集團(tuán)監(jiān)控服務(wù),可以在平臺上實(shí)時監(jiān)控函數(shù)運(yùn)行。
4 技術(shù)難點(diǎn)
函數(shù)執(zhí)行引擎設(shè)計(jì)
函數(shù)執(zhí)行引擎是整個FaaS的核心部分,負(fù)責(zé)函數(shù)實(shí)例的加載、預(yù)熱、調(diào)度執(zhí)行、卸載等生命周期管理。FaaS的函數(shù)目前支持Groovy語言,選擇Groovy主要是由于JVM提供的運(yùn)行時環(huán)境天然支持Groovy語言的運(yùn)行。FaaS平臺上每個函數(shù)都具有一個自己獨(dú)立的代碼版本庫,每次提交都將生成遞增的版本,執(zhí)行引擎加載函數(shù)實(shí)例時會從版本庫中加載當(dāng)前最新版本的代碼,通過初始化、預(yù)編譯等操作生成函數(shù)的實(shí)例放到實(shí)例池中,由于每個函數(shù)都有唯一標(biāo)識,因此,當(dāng)調(diào)用某個具體的函數(shù)時,執(zhí)行引擎會從實(shí)例池中取出對應(yīng)實(shí)例加載執(zhí)行。整個流程如下圖所示:
由于函數(shù)實(shí)例都存在于同一個JVM進(jìn)程中,并且不同于服務(wù),函數(shù)的粒度更小,因此函數(shù)的生命周期需要嚴(yán)格控制,不然大量函數(shù)加載到內(nèi)存中,有可能出現(xiàn)內(nèi)存占用過大的問題。同時兼顧SDK調(diào)用方式,防止多個函數(shù)常駐內(nèi)存將宿主應(yīng)用的內(nèi)存耗盡。所以目前采用了懶加載機(jī)制,按需加載函數(shù)實(shí)例到內(nèi)存中,過期自動回收,有助于釋放內(nèi)存提高內(nèi)存利用率。
每個Groovy函數(shù)對應(yīng)一個Groovy的解釋器環(huán)境GroovyEngine,不同的函數(shù)之間相互獨(dú)立,每個函數(shù)在加載到內(nèi)存的過程中都分別獨(dú)立的進(jìn)行預(yù)編譯,初始化等流程,防止不同函數(shù)之間相互干擾,同時為二三方JAR包加載提供隔離的環(huán)境,防止出現(xiàn)不同函數(shù)之間的類加載器相互影響的情況。
二三方JAR包加載能力
FaaS平臺提供二三方JAR包的加載能力,允許在不重啟整個底層容器的情況下,加載函數(shù)自己的二三方依賴,我們通過實(shí)現(xiàn)Groovy二三方JAR包加載能力的Classloader,實(shí)現(xiàn)了函數(shù)與函數(shù)之間、函數(shù)不同版本之間的二三方依賴加載能力。FaaS平臺的Classloader體系:
三 FaaS平臺的落地探索
結(jié)合目前阿里文娛業(yè)務(wù)的特點(diǎn),即大多以內(nèi)容分發(fā)為主,以首頁、二級頁等業(yè)務(wù)來看,內(nèi)容分發(fā)具有運(yùn)營坑位多、需求變化快、數(shù)據(jù)源多等特點(diǎn),傳統(tǒng)的Java服務(wù)端開發(fā)方式,前后端聯(lián)調(diào)以及后端開發(fā)部署都逐漸成了影響迭代效率的重要瓶頸,以往都是服務(wù)端開發(fā)在客戶端發(fā)版前發(fā)布線上,發(fā)布耗時長,回滾成本高,因此通過引入FaaS,希望提高服務(wù)端開發(fā)的靈活性,讓開發(fā)者更多的面向業(yè)務(wù)邏輯而不是花較大量的時間在服務(wù)的部署維護(hù)上面。
優(yōu)酷內(nèi)部的內(nèi)容分發(fā)目前主要在統(tǒng)一的內(nèi)容搭建投放框架之上開發(fā),這套框架是一套流程編排的框架,通過流程編排,從不同數(shù)據(jù)源獲取內(nèi)容,通過業(yè)務(wù)邏輯處理,最終通過模版字段映射輸出API內(nèi)容。目前FaaS主要應(yīng)用在數(shù)據(jù)源及模版字段映射階段。數(shù)據(jù)源即原始數(shù)據(jù)接口的封裝,通過數(shù)據(jù)源獲取實(shí)際業(yè)務(wù)需要的原始數(shù)據(jù),比如媒資節(jié)目視頻、節(jié)目專題數(shù)據(jù)、用戶關(guān)注等業(yè)務(wù)數(shù)據(jù);模版字段映射主要通過編寫Java的函數(shù)根據(jù)實(shí)際業(yè)務(wù)邏輯生成字段內(nèi)容。以往的開發(fā)模式下,如果業(yè)務(wù)邏輯有變化,需要變更然后發(fā)布Java應(yīng)用才能生效,采用FaaS開發(fā)之后,只需要發(fā)布對應(yīng)的FaaS函數(shù)即可,由于FaaS函數(shù)的發(fā)布是秒級,因此極大的提高了迭代效率。
1 統(tǒng)一的數(shù)據(jù)源封裝
我們使用FaaS實(shí)現(xiàn)數(shù)據(jù)源接口的封裝,當(dāng)有新的數(shù)據(jù)接口需要接入時,直接在FaaS平臺上通過編寫函數(shù)實(shí)現(xiàn),可以做到在本地Java應(yīng)用不發(fā)布的情況下,直接上線新數(shù)據(jù)源。對于新業(yè)務(wù)接口的快速接入具有重要意義。同時這些數(shù)據(jù)源可以被重用,因此在多人協(xié)作的模式下,通過復(fù)用函數(shù)實(shí)現(xiàn)的數(shù)據(jù)源極大的減少了重復(fù)開發(fā)量。
2 FaaS函數(shù)處理API協(xié)議模版字段映射

我們擴(kuò)展了搭投框架,通過Faas的SDK,服務(wù)端接口的模版解析階段除了能解析普通的Java函數(shù),也可以支持解析FaaS函數(shù),這類函數(shù)的代碼不是通過原生Java代碼編寫,而是在Faas平臺上用Groovy代碼編寫而成,這類函數(shù)的特點(diǎn)是編寫、更新、發(fā)布均不需要重新部署哥倫布業(yè)務(wù)應(yīng)用,只需要在Faas平臺上操作函數(shù)即可。字段邏輯的修改可以完全不用重啟Java應(yīng)用,快速應(yīng)對迭代變更。每個函數(shù)都有獨(dú)立的生命周期和發(fā)布流程,不同函數(shù)的發(fā)布變更之間相互隔離。當(dāng)有字段邏輯的變化時,可以完全不重啟本地Java應(yīng)用,直接通過函數(shù)的秒級發(fā)布來完成,極大提高了迭代效率。
四 總結(jié)與展望
目前優(yōu)酷內(nèi)容分發(fā)相關(guān)業(yè)務(wù)已經(jīng)陸續(xù)引入FaaS能力,在FaaS的助力下,迭代效率提升。但是平臺整體上還處于剛剛起步階段,也是我們Serverless實(shí)踐的初步嘗試。后續(xù)我們希望在以下幾個方面繼續(xù)探索FaaS平臺的技術(shù)與落地:
- 支持更多編程語言的運(yùn)行時環(huán)境,以及更友好的云端IDE開發(fā)體驗(yàn)。
- 優(yōu)化函數(shù)運(yùn)行集群的資源調(diào)度策略,合理分配函數(shù)執(zhí)行需要的資源,支持動態(tài)擴(kuò)縮容。
- 結(jié)合內(nèi)容分發(fā)業(yè)務(wù)的特點(diǎn),尋找更多業(yè)務(wù)的切入點(diǎn),通過FaaS進(jìn)一步提升現(xiàn)有技術(shù)架構(gòu)的靈活性和迭代效率。