毫秒級壓榨:小米函式計算背後的效能優化(二)
在上一篇文章 ofollow,noindex"> 毫秒級壓榨: 小米函式計算背後的效能優化(一) 中我們提到程式碼快取,容器快取,建立通用容器池等優化方法極大地提高了平臺的響應速度。
在這篇文章裡面,我們會提及以下三點優化,在降低響應延遲,提高qps值和節省計算資源方面有突出的效果。
圖一:大體架構
runc pause/resume 替換 docker pause/unpause
▲▲▲
每一個函式的第一次呼叫完成後,平臺會凍結這個容器,在第二次呼叫的時候再解凍來複用。一開始平臺使 用的是do cker pause/unpause命令,經過實踐,發現這兩個命令耗時是極不穩定的,從10ms到3秒不等。後來調研發現 runc pause/resume也能做到相同效果,且耗時大大降低,使用runc pause/resume 替換 docker pause/unpause,時間能穩定在100ms以內。
增加兩級快取!減輕資料庫壓力
▲▲▲
在函式計算場景裡,使用者函式的資訊是查詢多修改少,如圖一所示,隨著函式的qps上升,Router和Executor模組會頻繁請求資料庫獲取函式的資訊。資料庫的讀壓力隨著函式的qps呈線性增長。
首先我們增加了redis快取,但redis的讀壓力過大,很快就成為了系統的瓶頸,雖然可以增加redis從庫的數量來減輕讀壓力,但是這樣做會使成本增加。
通過分析,即使使用者在修改了函式的資訊,延遲一定時間生效也是可以接受的,只是在一段時間內某些應用例項會獲取到舊的函式資訊,函式的執行不會受到影響,所以在Router 和Executor裡面增加了本地快取,採用超時淘汰策略,這樣既消除了讀redis和資料庫時帶來的網路開銷,同時極大減輕了redis的讀壓力。
之前redis的讀壓力隨著函式qps線性增長,增加本地快取後,redis的qps計算公式就接近於(函式數量/快取超時秒數*(Router + executor數量))。在100個函式,Router數量為10,Rxecutor的數量為90,快取超時時間為10秒的情況下,redis的最大qps約為:(100/10*(10+90))=1000。
增加排程器模組, 提高容器的重用率
▲▲▲
因為Executor是負責準備函式的執行資源的,所以隨著函式的qps上升,Executor的負載就會顯著地增高,那麼就要對Executor擴容。根據圖一,Router通過兩種方式訪問Executor,其中通過佇列的請求是隨機發送到一個Executor上的。隨著Executor的部署數量增多,某些qps不高的函式的容器複用概率就會降低,會造成多次重新準備資源,不僅增加了響應延時,還浪費計算資源。
相對於Executor而言,Router模組上的負載明顯較低,需要部署的數量較少。利用這個特點,在Router裡面增加一個排程器模組,實現方式就是在Router裡保留一份容器的資訊,使用LRU的清理策略,根據保留的資訊將請求排程到Executor。
Router裡快取的容器資訊如圖二所示,使用了一個map,key是函式的id,value是一個連結串列結構,連結串列上的每一個node記錄著容器的id、Executor的地址和容器的失效時間。當有請求來的時候,挑選最後一個被使用過的容器資訊,將其從快取中取出,根據記錄上Executor的地址,通過rpc呼叫直接訪問容器所在的Executor,使用該容器來執行函式來處理請求。
圖二:Router裡快取的容器資訊
排程的具體流程如下:
-
某函式請求到達Router, 檢查是否有之前使用過的容器資訊,不能則轉到2, 能則從連結串列的尾部中取出最後一個被使用過的容器,轉到3。
-
Router將請求推送到訊息佇列,供下游的Executor接收,轉到4。
-
Router解析容器資訊,rpc呼叫管理該容器的Executor, 轉到5。
-
Executor檢查自身,如果有剩餘計算資源,就從佇列中拉取一個請求,啟動容器執行函式,返回給Router, 轉到7。
-
Executor接收來自Router的請求,檢查容器是否已被銷燬,如果是,返回容器不命中,否則用該容器接收請求執行,保留容器,返回結果給Router,轉到6。
-
Router接收來自Executor的返回,如果容器沒命中,轉到2,否則轉到7。
-
Router接收來自Executor的返回,將使用過的容器資訊新增到連結串列尾部,返回此次請求的結果。
對於低負載的一些函式請求,第二次請求的容器複用概率從 P1=(1/Executor數量)變成了 P2=(1/Router數量)。
在部署2臺Router,7臺Executor,執行41個函式,呼叫頻率分別從一秒鐘10次到一天5次之間的情況下測試。加上這個排程器優化之後,函式總體的容器重用率從原來的50%到了99%,除了一開始的呼叫,呼叫頻率較高的函式後面都是在複用之前的容器,只有呼叫頻率過低的函式,例如兩分鐘一次的,才會多次重新準備資源。
相關閱讀
小米函式計算觸發器再添新成員-TalosTrigger和EMQTrigger
戳原文,瞭解更多“小米函式計算”!