
一 背景
優酷内容分發業務涵蓋了優酷主客的首頁、頻道頁、二級頁等不同場景下的内容分發,服務端之前采用傳統的Java應用結合阿裡集團中間件的開發模式,一直是産品評審、API設計、前後端聯調、前後端發版等節奏。然而,随着端上内容的多樣化,産品需求疊代的加速,傳統的服務端架構開發模式已顯得力不從心,我們雖然沉澱出一套通用架構,但受限于開發模式的本質并沒有變化,業務開發的靈活性與開發成本依然很高。總結起來,面臨的挑戰主要是:API依賴資料源多,業務需求變化快,前後端聯調成本大等。
随着Serverless技術的發展,FaaS的相關實踐探索都在阿裡内部逐漸多起來,我們思考了FaaS的特點和面臨的挑戰,希望通過FaaS技術的引入,把一系列基礎能力沉澱下來,在此之上,通過FaaS來承接上層業務邏輯,阿裡巴巴文娛優酷FaaS平台應運而生。
二 平台設計與技術難點
1 設計目标
希望實作一個通用的函數計算平台,在這個平台上,開發者直接通過編寫、運作和管理一個或多個函數對外提供服務,允許通過微服務、HTTP接口、事件源觸發等多種方式調用函數。同時,函數的開發及釋出應該是秒級生效,且無需重新開機宿主應用的,這樣就可以克服傳統Java應用釋出部署的時間成本,極大的減輕開發者在代碼開發之外的時間成本,同時可以快速復原。
FaaS平台應該提供函數式應用的運作環境,應該支援輕量級腳本語言編寫函數。我們首選Groovy語言,主要是考慮了Groovy的代碼簡潔,同時可以通路Java的原生的類和對象。
FaaS可以根據實際的通路情況進行函數執行個體的動态加載和資源配置設定。
總結起來,在FaaS平台上運作的函數應該是一個短小、離散、可複用的代碼塊,我們希望它有以下幾個特點:
- 生命周期短,支援快速釋出部署
- 非守護程序(不需要長時間運作,按需加載)
- 不提供長連接配接服務
- 無狀态
- 可重用現有服務或第三方資源(重點,FaaS應該建立在完善的基礎服務上)
- 毫秒級執行時間
2 平台整體設計
FaaS平台的整體核心架構主要由網關、運作時容器、一站式運維釋出平台、基礎服務等組成:
網關層主要負責接受函數調用請求,通過函數的唯一辨別及函數的叢集資訊分發函數調用到對應叢集的機器環境中執行。
函數容器層是整個系統的核心,主要通過函數執行引擎進行執行個體的調用執行,同時負責函數執行個體的生命周期管理,包括按需加載、代碼預熱、執行個體解除安裝回收等工作。
一站式釋出運維平台(FaaS Platform)是面向開發者的主要操作平台,開發者在平台上進行函數編寫、版本送出釋出、復原、監控運維等一系列工作。整個監控體系打通了集團的基礎服務監控體系,,可以提供實時大盤,叢集性能等基本監控名額的查詢功能。
整個FaaS平台建立在集團中間件以及優酷内容分發依賴的各基礎服務之上,通過良好的封裝向開發者提供簡潔的服務調用方式,同時函數本身的執行都是運作在互相隔離的環境中,通過統一的函數執行個體管理,進行函數的排程、執行監控、動态管理等。
整體技術棧服務端容器層主要是采用Java實作,結合集團中間件完成整個容器層的主要功能。
前端主要基于React架構和Dva狀态管理架構實作。當然,在實際開發過程中我們選擇了螞蟻金服的Bigfish架構和Odin腳手架。React提供了元件化的概念,這意味着我們開發的元件可以像HTML基本DOM元素一樣不斷被複用。為了實作元件的複用化和研發效率的提升,Bigfish在Web頁面上進行了分層設計,細粒度從大到小依次為:頁面模闆 -> 區塊 -> 業務元件 -> 元件。Odin腳手架是優酷推出一款面向中背景業務系統的前端開發腳手架,內建了Bigfish的架構,支援以配置化的方式建構網站路由,使得開發者不需要關注過多底層細節,可以快速上手實作業務邏輯和頁面建構。
類似于服務端側的MVC分層模式,前端在實作業務邏輯和資料通信時也有對應的封層設計模式,來實作元件的狀态管理。經曆了從Flux -> Redux -> Dva的衍變,狀态管理機制對複雜業務帶來的益處正在不變突出。Dva的完整資料流圖如下:
State是負責儲存整個應用狀态,View是React元件構成的視圖層,Action是描述事件的對象。connect方法是綁定 State 到 View的函數,使得View層的元件可以動态監聽State中的屬性,同時可以通過dispatch方法負責将Action發送至State觸發狀态改變。觸發狀态改變有兩種類型的函數:effect函數和reducer函數。前者會與服務端進行資料通信,可以處理異步動作;後者處理同步動作,并直接更新State。
FaaS Platform前端主要分為函數建立、函數管理、函數釋出、函數模闆和應用統計五個子產品。在FaaS Platform系統中,函數是對外可被排程的最小單元,而應用是劃分機器資源的最小機關,是以我們設定應用與函數存在一對多的映射關系。
函數建立子產品
函數建立子產品主要提供添加函數的功能。一個完整函數必須包括函數名稱、函數辨別、函數類型、函數所屬應用及應用下所屬分類等基本資訊;同時類似于mtop網關,我們提供對于函數入參、響應業務結果、響應業務錯誤碼的配置頁面,用于自動生成函數調用入參表單和函數接口文檔。函數的英文辨別唯一确定一個函數,不可重複。
函數管理子產品
函數管理子產品主要提供函數的CRUD操作和函數的線上編寫功能。在本頁面我們可以快速進行複雜條件的函數查詢和函數基本資訊和狀态的編輯。同時我們提供函數編寫的線上Web IDE,支援檔案增删、代碼編寫、自動儲存、函數送出、函數調試、日志列印等功能。
函數釋出子產品
函數釋出子產品主要提供函數送出曆史的查詢和執行函數釋出的功能。我們像傳統Java應用支援引入二三方依賴,但不同于傳統的Java應用釋出,FaaS Platform系統中的函數釋出可以實作秒級釋出。目前函數釋出已經支援函數復原釋出和函數分批次釋出,從部署環節實作對複雜多變業務需求的快速響應。
函數模闆子產品
函數模闆子產品主要提供函數模闆的CRUD操作和函數的線上編寫功能。結合實際的業務場景,我們首先提供一些基礎的内置模闆,友善函數的快速初始化。同時對于某一個業務問題的完整解決方案,我們允許該函數儲存為自定義的函數模闆。函數模闆的Web IDE同樣支援函數模闆的線上編寫、調試、自動儲存等功能。
應用統計子產品
由于函數隸屬于應用進而具備機器資源,我們計劃提供應用統計子產品以應用為拆分進行函數上線狀态、釋出版本的資料統計;同時我們也基于函數日志提供函數調用情況(調用量、成功率、響應時間)的統計分析和監控。關于具備的細節,我們正在逐漸實作和完善。
3 主要特性
優酷FaaS平台的主要特性是開發接入低成本、函數運作時環境隔離以及運維監控操作的透明化。
開發接入低成本
FaaS平台通過一站式的雲端開發平台,使使用者可以直接面向業務邏輯的開發,而無需關注基礎服務及中間件的依賴,平台本身提供完善的基礎能力封裝,包括:快捷開發能力,中間件快速接入能力,資料存儲快速接入能力,基礎能力封裝直接調用等。
業務邏輯開發模式輕量化、無應用化,釋出復原秒級生效,極大的減輕了傳統服務端開發過程的繁瑣流程,将開發者的精力更多的集中于核心業務邏輯的開發。
同時提供如下的簡潔易于操作的開發部署流程設計,減輕開發者開發部署的時間成本。
FaaS平台上的函數除了開發成本低,調用者接入的方式也比較簡單。我們同時提供了中心化和去中心化兩種使用方式,不管去中心化還是中心化使用方式,函數代碼的編寫、調試、釋出均在一站式運維釋出平台上完成。在中心化接入方式下,我們通過統一的函數服務叢集提供對外服務,允許調用者通過統一的函數調用接口以HSF服務或者HTTP接口調用函數,而函數代碼的執行完全在我們的函數服務叢集上,開發者無需自己申請應用。
對于去中心化接入方式,開發者如果想調用函數平台上的FaaS函數,可以引入我們提供的SDK,此時,函數的執行完全在調用者應用的本地程序裡,FaaS平台隻提供函數的開發釋出功能。
運作時環境的隔離
運作時環境的隔離分為兩個層次,一個層次是函數容器内部函數執行個體之間的隔離;另外一個層次是不同函數本身就運作在不同的虛拟應用叢集上,叢集與叢集之間的隔離性。
函數容器内部函數執行個體的隔離指的是在FaaS平台上編寫的Groovy函數運作在統一的JVM程序中,每個函數在開發的過程中都會生成多個版本,而不同函數之間、同一函數的不同版本之間在運作時的環境都是互相隔離,互不幹擾的。
函數運作叢集的隔離性主要是根據函數的通路量、函數的服務特點(長尾服務還是通用服務)等特性,在函數建立之初就将函數綁定在不同的虛拟應用上,而不同的應用會運作在不同的機器叢集上,函數在被調用時,網關層可以根據函數的應用将函數的調用分發到不同的叢集上執行,保證函數之間實體隔離。
運維監控的透明化
FaaS平台的函數都能在平台上直接進行監控運維操作,我們通過在函數執行流程上收集函數的執行日志,并将日志實時上報到集團監控服務,可以在平台上實時監控函數運作。
4 技術難點
函數執行引擎設計
函數執行引擎是整個FaaS的核心部分,負責函數執行個體的加載、預熱、排程執行、解除安裝等生命周期管理。FaaS的函數目前支援Groovy語言,選擇Groovy主要是由于JVM提供的運作時環境天然支援Groovy語言的運作。FaaS平台上每個函數都具有一個自己獨立的代碼版本庫,每次送出都将生成遞增的版本,執行引擎加載函數執行個體時會從版本庫中加載目前最新版本的代碼,通過初始化、預編譯等操作生成函數的執行個體放到執行個體池中,由于每個函數都有唯一辨別,是以,當調用某個具體的函數時,執行引擎會從執行個體池中取出對應執行個體加載執行。整個流程如下圖所示:
由于函數執行個體都存在于同一個JVM程序中,并且不同于服務,函數的粒度更小,是以函數的生命周期需要嚴格控制,不然大量函數加載到記憶體中,有可能出現記憶體占用過大的問題。同時兼顧SDK調用方式,防止多個函數常駐記憶體将宿主應用的記憶體耗盡。是以目前采用了懶加載機制,按需加載函數執行個體到記憶體中,過期自動回收,有助于釋放記憶體提高記憶體使用率。
每個Groovy函數對應一個Groovy的解釋器環境GroovyEngine,不同的函數之間互相獨立,每個函數在加載到記憶體的過程中都分别獨立的進行預編譯,初始化等流程,防止不同函數之間互相幹擾,同時為二三方JAR包加載提供隔離的環境,防止出現不同函數之間的類加載器互相影響的情況。
二三方JAR包加載能力
FaaS平台提供二三方JAR包的加載能力,允許在不重新開機整個底層容器的情況下,加載函數自己的二三方依賴,我們通過實作Groovy二三方JAR包加載能力的Classloader,實作了函數與函數之間、函數不同版本之間的二三方依賴加載能力。FaaS平台的Classloader體系:
三 FaaS平台的落地探索
結合目前阿裡文娛業務的特點,即大多以内容分發為主,以首頁、二級頁等業務來看,内容分發具有營運坑位多、需求變化快、資料源多等特點,傳統的Java服務端開發方式,前後端聯調以及後端開發部署都逐漸成了影響疊代效率的重要瓶頸,以往都是服務端開發在用戶端發版前釋出線上,釋出耗時長,復原成本高,是以通過引入FaaS,希望提高服務端開發的靈活性,讓開發者更多的面向業務邏輯而不是花較大量的時間在服務的部署維護上面。
優酷内部的内容分發目前主要在統一的内容搭建投放架構之上開發,這套架構是一套流程編排的架構,通過流程編排,從不同資料源擷取内容,通過業務邏輯處理,最終通過模版字段映射輸出API内容。目前FaaS主要應用在資料源及模版字段映射階段。資料源即原始資料接口的封裝,通過資料源擷取實際業務需要的原始資料,比如媒資節目視訊、節目專題資料、使用者關注等業務資料;模版字段映射主要通過編寫Java的函數根據實際業務邏輯生成字段内容。以往的開發模式下,如果業務邏輯有變化,需要變更然後釋出Java應用才能生效,采用FaaS開發之後,隻需要釋出對應的FaaS函數即可,由于FaaS函數的釋出是秒級,是以極大的提高了疊代效率。
1 統一的資料源封裝
我們使用FaaS實作資料源接口的封裝,當有新的資料接口需要接入時,直接在FaaS平台上通過編寫函數實作,可以做到在本地Java應用不釋出的情況下,直接上線新資料源。對于新業務接口的快速接入具有重要意義。同時這些資料源可以被重用,是以在多人協作的模式下,通過複用函數實作的資料源極大的減少了重複開發量。
2 FaaS函數處理API協定模版字段映射
我們擴充了搭投架構,通過Faas的SDK,服務端接口的模版解析階段除了能解析普通的Java函數,也可以支援解析FaaS函數,這類函數的代碼不是通過原生Java代碼編寫,而是在Faas平台上用Groovy代碼編寫而成,這類函數的特點是編寫、更新、釋出均不需要重新部署哥倫布業務應用,隻需要在Faas平台上操作函數即可。字段邏輯的修改可以完全不用重新開機Java應用,快速應對疊代變更。每個函數都有獨立的生命周期和釋出流程,不同函數的釋出變更之間互相隔離。當有字段邏輯的變化時,可以完全不重新開機本地Java應用,直接通過函數的秒級釋出來完成,極大提高了疊代效率。
四 總結與展望
目前優酷内容分發相關業務已經陸續引入FaaS能力,在FaaS的助力下,疊代效率提升。但是平台整體上還處于剛剛起步階段,也是我們Serverless實踐的初步嘗試。後續我們希望在以下幾個方面繼續探索FaaS平台的技術與落地:
- 支援更多程式設計語言的運作時環境,以及更友好的雲端IDE開發體驗。
- 優化函數運作叢集的資源排程政策,合理配置設定函數執行需要的資源,支援動态擴縮容。
- 結合内容分發業務的特點,尋找更多業務的切入點,通過FaaS進一步提升現有技術架構的靈活性和疊代效率。