阿里妹導讀:FaaS—Function as a service,函式即服務。它是2014年由於亞馬遜的AWS Lambda的興起,而被大家廣泛認知。FaaS能力是NBF中的一項非常重要的能力,NBF是一個非典型的FaaS架構,但是具備了典型的FaaS能力。文章將詳細介紹NBF的FaaS容器架構、服務釋出、服務路由和強大的Serverless能力以及NBF-FaaS在阿里大促期間的實踐心得。
NBF (New-Retail Business Framework) 是供應鏈中臺基礎技術團隊研發的新零售服務開放框架,提供了標準化業務定義、快捷服務開發和生態開放的能力,旨在為生態夥伴提供一整套完整的新零售PaaS和SaaS的解決方案。
2.FaaS
FaaS是Serverless的一種典型形式,由 Serverless平臺提供負載均衡、高可用、自動擴縮容、服務治理等最佳實踐,將這些最佳實踐對 Developer 透明化,進一步縮短 Developer 從想法到產品的時間,降低開發成本,同時保障 Developer 開發的服務的可靠性。透過事件驅動的方式,開發者的Function透過Event有效觸發,比如 HTTP 請求、訊息事件等。Event Sources Function 事件驅動的集合;
Function Instances 提供服務的Function或微服務;
FaaS Controller 管理Function的控制服務,比如典型的API Gateway或者BFF(Backend For Front)等;
Platform Services Function依賴的平臺服務,如許可權管理 API、物件儲存服務等。
3.NBF-FaaS架構
3.1 NBF-Platform Services(1)Serverless平臺—CSE(Cloud Service Engine):Serverless是FaaS平臺依賴的基礎能力,這一塊NBF與中介軟體CSE團隊深度合作,CSE提供快速的擴縮容的能力,可以在毫秒級別支援並行水平擴容和動態縮容,滿足業務的錯峰場景。基礎技術團隊與CSE共建容器冷熱啟動的效能最佳化以及Serverless運維工具(日誌,監控報警,鏈路跟蹤等)開發。(2)NBF容器:NBF容器採用OSGI架構,提供了Bundle完整的生命週期管理,包括Bundle的載入,啟動,解除安裝和登出等等,以及容器和Bundle的隔離和通訊能力。服務釋出:把Function/Bundle快速釋出成服務的能力。
服務路由:服務多型,降級和Mock的路由能力。
服務管理:基於SPI和Bundle的版本管理和服務啟停能力。
服務運維:服務的Serverless能力,混部能力,灰度能力和容災降級能力等 。
3.2 NBF-Function InstancesNBF的函式例項對應的就是長在NBF服務市場的一個個服務背後的Bundle實現:NBF的Event Sources是由流程中心提供的基於EventMap的服務編排能力:NBF的FaaS Controller包括三種型別:(1)流程中心排程服務 流程中心提供了排程SPI服務的事件驅動能力,包括流程事件,訊息事件,定時事件等等,目前基本可以覆蓋所有的事件驅動的業務場景。(2)Broker呼叫RPC服務 Broker支援多模式呼叫Bundle釋出的RPC服務,包括服務的多型路由,降級路由和Mock路由等 。(3)NBF-Rest呼叫HTTP服務,NBF-Rest支援呼叫Bundle釋出的HTTP服務。NBF容器管理了Bundle的載入,啟動,解除安裝和登出的完整週期,並採用OSGI機制實現了容器和Bundle之間的隔離和通訊能力。這一層NBF和CSE團隊共建,CSE負責實現Fast Auto-Scaling,目前已經在雙十二和女王節等大促活動得到了充分驗證。NBF實現了Fast Cold Start和Fast Hot Start Fast Cold Start—最佳化Bundle服務釋出的冷啟動時間 Fast Hot Start—最佳化Bundle服務Scaling up後的服務可用時間 底層依賴的CaaS服務目前也在跟隨CSE的節奏從Sigma3.0向ACK-EE遷移,未來將全面支援阿里雲單元。NBF-OSGI Framework是採用OSGI機制實現了Bundle載入,啟動,解除安裝和登出完整生命週期託管,目前在集團內部絕對多數都是Pandora應用,集團中介軟體都是透過ModuleClassLoader外掛式載入,因此目前NBF容器的Bundle載入方式也是建立在Pandora的載入機制之上的NBF-OSGI Framework提供了一整套Bundle的隔離機制,Bundle與容器的通訊機制以及Bundle之間的通訊機制。<imported>
<packages>
<package>org.springframework</package>
<package>org.apache.commons.logging</package>
<package>org.aopalliance</package>
<package>org.aspectj</package>
</packages>
</imported>
Bundle無需多言,是業務方編寫的業務邏輯程式碼 Plugin是NBF引擎提供的增值能力,採用外掛化的方式進行載入,比如NBF-FaaS能力中最核心的服務釋出能力。前面提到了由於目前集團內部絕對多數都是Pandora應用,因此目前NBF的容器架構是建立Pandora的載入機制上的,本質上是Run在Pandora的容器內的。而未來的NBF容器架構是由NBF-OSGI Framework來託管外部容器,這些外部容器可以是Pandora容器,也可以是非Pandora容器,這樣就實現了NBF容器對於Pandora容器的依賴倒置。而對於Run在NBF-FaaS平臺的Bundle而言就具有更豐富的可變性。服務釋出的核心原理如下圖比較詳細的介紹了NBF容器把Bundle釋出成RPC服務的完整鏈路,核心主要包括三步:Broker Agent實現了Broker的SPI和Implement的分離,透過BrokerBundleLoader動態載入implement,這樣Broker的版本升級對於使用方而言是不用做程式碼變更和重新發布。想想某些重型的二方庫版本升級,每個業務方都需要深度感知,是不是覺得會舒爽很多。SPI Proxy則實現了採用註解的方式來實現無侵入的服務呼叫,從傳統的服務呼叫方式遷移到NBF的服務呼叫方式易如反掌。舉個例子:@Autowired
ServiceA serviceA;
serviceA.invoke(params);
@Autowired
BundleBroker bundleBroker;
bundleBroker.get(ServiceA.class).invoke(params);
@DynamicInject
ServiceA serviceA;
serviceA.invoke(params);
有了@DynamicInject,是不是覺得NBF服務呼叫跟原有的傳統呼叫方式沒啥區別, 對於@DynamicInject支援的幾種方式。對於Broker Bundle核心功能包括以下幾塊核心功能:4.3.1.1 BundleProxy
BundleProxy可以簡單理解成是對Bundle執行的代理機制,比如Bundle的主動熔斷和被動降級這些能力都是透過BundleProxy實現的,因為這些特性對於每個執行Bundle都是統一的機制。
4.3.1.2 服務發現
服務發現主要職責怎麼找到需要呼叫的服務,怎麼生成呼叫服務的URI,拿HSF的服務定址策略來對比,整個機制就簡單了 HSF的定址URI: Proxy://IP:port/service/version/method 對於Broker服務發現 IP和port對應的容器本身的網路資訊,這些可以透過APPName或者Armory分組以及未來serverless後的GroupId來獲取 serviceName,version這些資料就是第三層Broker資料層提供的spi和bundle後設資料資訊 有了這些基本資訊以後,我們就可以生成NBF服務發現的URI了。
4.3.1.3 路由計算
在介紹路由計算之前,先介紹下先前提到@DynamicInject支援的幾種方式:預設模式,規則模式和動態模式。
(1)預設模式: 預設模式就是服務呼叫不需要指定任何路由引數,這種呼叫方式適合單Bundle實現的SPI,Bundle實現就是預設實現
@DynamicInject
private ConfigReadService configReadService;
ResultDO<List<ConfigDTO>> result = configReadService.queryConfig(new ConfigQuery);
(2)規則模式: 規則模式支援三種方式指定路由引數:Id(業務身份),Expression(正規表示式),Rule(規則表示式)。
// 指定bundleId方式, type預設為ID
@DynamicInject(pattern = "drf")
// 指定正則方式
@DynamicInject(pattern = "^drf-hz.*$", type = "REG")
// 指定Rule方式
@DynamicInject(pattern = "{\"wareHouseId\":\"2001\"}", type = "RULE")
(3)動態模式: 動態模式指的是編碼時無法確定呼叫引數的場景,路由引數需要呼叫時傳入。
@DynamicInject
private DynamicInvoker<ConfigReadService> configReadServiceDynamic;
ResultDO<List<ConfigDTO>> result;
// 動態傳入bundleId
result = configReadServiceDynamic.getService(bundleId).queryConfig(new ConfigQuery);
// 動態傳入規則引數
Map<String, Object> params = new HashMap<>();
params.put("merchant", merchant);
result = configReadServiceDynamic.getService(params).queryConfig(new ConfigQuery);
路由計算又要再次提到我們先前提到過的SpiProxy了,SpiProxy的職能主要有兩個:a.獲取SPIInfo,包括SPI的ClassName,SpiVersion和SpiCode等等b.根據路由引數計算需要呼叫BundleId 然後再根據我們在服務發現中提到的定址策略,不難發現我們已經可以生成NBF服務呼叫的URI,這就是NBF多型路由的核心原理 。4.1.3.4 熔斷降級
熔斷降級包括兩個核心能力:被動降級和主動熔斷。
(1)被動降級:被動降級會在三種情況下觸發:服務找不到,服務返回異常和服務超時,這個時候服務呼叫會自動路由到Bundle對應的降級Bundle。用個簡單的表格解釋下降級的含義 :(2)主動熔斷:主動熔斷是透過NBF設定基線指標來實現的,如果超過服務的基線指標,則路由到降級Bundle 。在截圖的栗子中我們選用Bundle(供應鏈-批發服務-大潤發實現)作為Bundle(供應鏈-批發服務-盒馬實現)的降級Bundle,在超過基線指標100ms就會路由到降級Bundle。對於熔斷降級實現的核心原理就是我們先前提到過的BundleProxy,這些特性對於每個執行Bundle都是統一的機制,透過BundleProxy識別是否滿足主動熔斷和被動降級的條件,然後再代理執行真正的Bundle 。4.1.3.5 流量管控
流量管控提供了一種軟負載的能力,支援設定Bundle和降級Bundle之間的流量配比,我們仍以Bundle:供應鏈-批發服務-盒馬實現為例,以圖為證:
在先前提到的NBF容器架構中,NBF-Serverless能力是NBF容器架構的重要基石,只有在Serverless實現毫秒級彈性擴縮容前提下,才能真正支撐錯峰場景,才能最大程度的節約機器資源。只有在Serverless實現服務資源統一彈性排程的前提下,才能真正實現NBF的服務部署隔離,而不是目前透過定製容器規格(1Core2G,2Core4G,4Core8G等等)和Bundle混部的方式來實現Bundle部署隔離和機器資源之間的平衡。在這裡一定要為NBF的深度合作伙伴——CSE團隊鼓個掌,他們已經具備了毫秒級Auto-Scaling能力,為我們提供了可靠的基礎設施。當然對於Serverless配套運維設施(日誌,監控報警,鏈路跟蹤等)和Serverless遷移到ACK-EE雲單元這些事情,NBF和CSE都還在路上。那在NBF-Serverless能力的建設過程中,NBF又扮演什麼角色呢?用一張圖來簡單表述下Serverless的實現原理以及CSE與NBF的職責劃分。 ★ 5.1.1 Fast Auto-ScalingFast Auto-Scaling是CSE提供的核心基礎設施能力,毫秒級的彈性擴容主要包括幾個步驟:(1)種子機器的啟動 種子機器的啟動就是冷啟動的過程,這個過程跟當前集團APP啟動的方式無異,就是容器啟動,映象載入和服務暴露的幾個步驟,因此冷啟動的時間普遍來說是分鐘級別的。(2)種子分發 透過Fork2的技術實現了種子機器的記憶體複製,而把記憶體複製到擴容機器上的時間是極短的,因此CSE的Auto-Scaling可以毫秒級實現並行水平擴容。(3)服務註冊 這個過程實際上就是在ConfigServer完成服務註冊,從而可以保障複製出來的Service Bean是可被呼叫的。NBF在NBF-Serverless能力構建中第一個重要事項就是實現冷啟動最佳化,我們期望把冷啟動的啟動從分鐘級別最佳化到秒級,因此調整了NBF Bundle的冷啟動機制:(1)在Bundle建立機器分組和擴容分組的時候提前部署Engine。(2)透過NBF的FaaS能力動態載入Bundle,原來,冷啟動時間=Pandora容器的啟動時間+Engine的啟動時間+Bundle的install和start時間,經過最佳化以後,冷啟動時間=Bundle的install和start時間。
★ 5.1.3 Fast Hot Start
由於當前的擴容機制是透過記憶體複製實現的,而類似於UUID這種與機器有關的記憶體變數的複製是不合適的,因此NBF的熱啟動最佳化主要是提供了refresh記憶體變數的機制。NBF的Framework託管了Bundle生命週期管理,也提供相應的Hook能力,透過這些Hook就能解決UUID這種問題。雖然目前Serverless運維配套能力還不夠完善,但是我們仍然在去年雙十二和今年女王節上線了幾個P0級服務,驗證在大促場景下Serverless的穩定性和毫秒級的Auto-Scaling能力。當然我們敢在S級的大促中驗證P0級服務也是有所依仗的,那就是NBF的熔斷降級和流量管控能力。文描服務在女王節當天的QPS流量從4000+飆升到12萬,Serverless非常迅速的擴容到10臺,妥妥的支撐了業務峰值。而對於機器資源的節約就顯而易見了,原來文描服務根據業務體量常態部署的10臺,而Serverless目前只需要常態部署2臺(其實可以只部署1臺,2臺可以認為是容災),而終態Serverless將解決長尾服務的問題,最終可以縮容到0臺,這樣對機器資源是更大程度的節約。下圖是女王節期間的Serverless前後的指標體系對比 :從圖中的資料可以看出,整個文描服務在大促期間表現出來的系統穩定性和服務穩定性是完全可靠的,這也就充分驗證NBF-Serverless的可行性。極速回滾是NBF服務高可用運維一種非常有效的手段。傳統的APP回滾方式是重新編譯、構建、打包和部署,而NBF具備典型的FaaS能力,對於Bundle回滾只需要重新load指定回滾版本的Jar包而已,而NBF Engine又是常駐容器,因此Bundle回滾速度是非常之快的。 文章比較詳細的介紹了NBF的FaaS能力,一句話總結:NBF是非典型的FaaS架構,但是具備典型的FaaS能力。開篇介紹了業界對於FaaS的廣泛定義,然後對比了FaaS典型架構和NBF-FaaS的非典型架構之間的關係,之後重點介紹NBF的FaaS能力,包括NBF的容器架構,Bundle的服務釋出和Bundle路由與管控的核心實現原理。最後表述了NBF的高可用運維能力,重點表述了NBF-Serverless的實現原理和具體實踐心得。現在NBF從生長的盒馬迴歸到供應鏈中臺,為包括盒馬在內的25個BU和合作夥伴提供生態開放能力。