Serverless 應用優化四則祕訣

Serverless發表於2022-02-07

Serverless 架構下,雖然我們更多精力是關注我們的業務程式碼,但是實際上對於一些配置和成本也是需要進行關注的,並且在必要的時候,還需要根據配置與成本進行對我們的 Serverless 應用進行配置優化和程式碼優化。

資源評估依舊重要

Serverless 架構雖然是按量付費的,但是並不代表他就一定比傳統的伺服器租用費用低,如果我們對自己的專案評估不準確,對一些指標設定不合理,Serverless 架構所產生的費用可能是巨大的。

一般情況下,FaaS 平臺的收費是和三個指標具有直接關係的:

所配置的記憶體規格;
程式所消耗的時間;
以及產生的流量費用。

通常情況下程式所消耗的時間可能會與記憶體規格、程式本身所處理的業務邏輯有關。流量費用與程式本身和客戶端互動的資料包大小有關,所以在這三個常見的指標,可能因為配置不規範導致計費出現比較大偏差的就是記憶體規格。以阿里雲函式計算為例,我們假設有一個 Hello World 的程式,每天都會被執行 10000 次,可以統計不同規格的例項所產生的費用(不包括網路費用):

阿里雲
通過上表可以看到,當程式在 128MB 規格的記憶體中可以正常執行,如果我們錯誤地將記憶體規格設定成了 3072MB,可能每月產生的費用將會暴漲 25 倍!所以我們在上線 Serverless 應用之前,要對資源進行評估,以便得到更合理的配置來進一步降低我們的成本。

合理的程式碼包規格

各個雲廠商的 FaaS 平臺中都對程式碼包大小有著限制,拋掉雲廠商對程式碼包的限制,單純地說程式碼包的規格可能會產生的影響,通過函式的冷啟動流程可以看到:

image
在函式啟動的過程中,有一個過程是載入程式碼的過程,那麼當我們所上傳的程式碼包過大,或者說檔案過多導致解壓速度過慢,就會直接導致載入程式碼這個過程變長,進一步直接導致冷啟動時間變久。

可以設想一下,當我們有兩個壓縮包,一個是隻有 100KB 的程式碼壓縮包,另一個是 200MB 的程式碼壓縮包,兩者同時在千兆的內網頻寬下理想化(即不考慮磁碟的儲存速度等)下載,即使最大速度可以達到 125MB/S,那麼前者的下載速度只有不到 0.01s,後者需要 1.6s。除了下載時間之外,還有檔案的解壓時間,那麼兩者的冷啟動時間可能就相差 2s。

一般情況下,一個傳統的 Web 介面,如果要 2s 以上的響應時間,實際上對很多業務來說是不能接受的,所以在我們打包程式碼時就要儘可能的降低壓縮包大小。以 Node.js 專案為例,打包程式碼包時,可以採用 Webpack 等方法,來壓縮依賴包大小,進一步降低整體程式碼包的規格,提升函式的冷啟動效率。

合理利用例項的複用

在各個雲廠商的 FaaS 平臺中,為了更好的解決冷啟動的問題,為了更合理的利用資源,是存在“例項”複用情況的。所謂的例項複用,就是當一個例項完成一個請求後並不會釋放,而是進入“靜默”的狀態。在一定時間範圍內,如果有新的請求被分配過來,則會直接呼叫對應的方法,而不需要再初始化各類資源等,這在很大程度上減少了函式冷啟動的情況出現。為了驗證,我們可以建立兩個函式:
函式1:

 -- coding: utf-8 --

def handler(event, context):
  print("Test")
  return 'hello world'​

函式2:

 -- coding: utf-8 --

print("Test")

def handler(event, context):
  return 'hello world'​

我們在控制檯多次點選“測試”按鈕,對這兩個函式進行測試,判斷其是否在日誌中輸出了“Test”,我們可以統計結果:


image
根據上面的情況,我們可以看到,其實例項複用的情況是存在的。因為“函式 2”並不是每次都會執行入口函式之外的一些語句。根據“函式 1”和“函式 2”,我們也可以進一步思考,如果 print("Test") 語句是一個初始化資料庫連線,或者是載入一個深度學習的模型,是不是“函式 1”的寫法就是每次請求都會執行,而“函式 2”的寫法是可以存在複用已有物件的情況?

所以在實際的專案中,有一些初始化操作,是可以按照“函式 2”來進行實現的,例如:

  • 機器學習場景下,在初始化的時候載入模型,避免每次函式被觸發都會載入模型帶來的效率問題,提高例項複用場景下的響應效率;
  • 資料庫等連結操作,可以在初始化的時候進行連結物件的建立,避免每次請求都建立連結物件;
  • 其他一些需要首次載入時下載檔案,載入檔案的場景,在初始化的時候進行這部分需求的實現,可以在例項複用的時候效率更高;

    善於利用函式特性

各個雲廠商的FaaS平臺都有一些“平臺特性”,所謂的平臺特性,是指這些功能可能並不是《CNCF WG-Serverless Whitepaper v 1.0》中規定的能力,或者描述的能力,僅僅是作為雲平臺根據自身業務發展和訴求,從使用者角度出發挖掘出來,並且實現的功能,可能只在某個雲平臺或者某幾個雲平臺所擁有的功能。這類功能一般情況下如果利用得當會讓我們的業務效能等有質的提升。

1、Pre-freeze & Pre-stop

以阿里雲函式計算為例,在平臺發展過程中,使用者痛點(尤其是傳統應用平滑遷移至 Serverless 架構)如下:

  • 非同步背景指標資料延遲或丟失:如果在請求期間沒有傳送成功,則可能被延遲至下一次請求,或者資料點被丟棄。
  • 同步傳送指標增加延遲:如果在每個請求結束後都呼叫類似Flush介面,不僅增加了每個請求的延遲,對於後端服務也產生了不必要的壓力。
  • 函式優雅下線:例項關閉時應用有清理連線,關閉程式,上報狀態等需求。在函式計算中例項下線時機開發者無法掌握,也缺少 Webhook 通知函式例項下線事件。

根據這些痛點發布了執行時擴充套件(runtime extensions)功能。該功能在現有的 HTTP 服務程式設計模型上擴充套件,在已有的 HTTP 伺服器的模型中增加了 PreFreeze 和 PreStop webhooks。擴充套件開發者實現 HTTP handler,監聽函式例項生命週期事件,如下圖所示:

image

  • PreFreeze:在每次函式計算服務決定冷凍當前函式例項前,函式計算服務會呼叫 HTTP GET /pre-freeze 路徑,擴充套件開發者負責實現相應邏輯以確保完成例項冷凍前的必要操作,例如等待指標傳送成功等。函式呼叫 InvokeFunction 的時間不包 PreFreeze Hook 的執行時間。


image

  • PreStop:在每次函式計算決定停止當前函式例項前,函式計算服務會呼叫 HTTP GET /pre-stop 路徑,擴充套件開發者負責實現相應邏輯以確保完成例項釋放前的必要操作,如關閉資料庫連結,以及上報、更新狀態等。


image

2、單例項多併發

眾所周知,各個廠商的函式計算通常是請求級別的隔離,即當客戶端同時發起 3 個請求到函式計算,理論上會產生三個例項來進行應對,這個時候可能會涉及到冷啟動問題,可能會涉及到請求之間狀態關聯問題等,但是部分雲廠商提供了單例項多併發的能力(例如阿里雲函式計算),該能力允許使用者為函式設定一個例項併發度(InstanceConcurrency),即單個函式例項可以同時處理多少個請求。

如圖下圖,假設同時有 3 個請求需要處理,當例項併發度設定為 1 時,函式計算需要建立3個例項來處理這 3 個請求,每個例項分別處理 1 個請求;當例項併發度設定為 10 時(即 1 個例項可以同時處理 10 個請求),函式計算只需要建立 1 個例項就能處理這 3 個請求。


單例項多併發效果簡圖

單例項多併發的優勢如下:

  • 減少執行時長,節省費用。例如,偏I/O的函式可以在一個例項內併發處理,減少例項數從而減少總的執行時長。
  • 請求之間可以共享狀態。多個請求可以在一個例項內共用資料庫連線池,從而減少和資料庫之間的連線數。
  • 降低冷啟動概率。由於多個請求可以在一個例項內處理,建立新例項的次數會變少,冷啟動概率降低。
  • 減少佔用 VPC IP 在相同負載下,單例項多併發可以降低總的例項數,從而減少 VPC IP 的佔用。

單例項多併發的應用場景是比較廣泛的,例如函式中有較多時間在等待下游服務的響應的場景就比較適合使用該種功能,但是單例項多併發也並不適合全部應用場景,例如當函式中有共享狀態且不能併發訪問的場景,單個請求的執行要消耗大量 CPU 及記憶體資源的場景,就不適合使用單例項多併發這個功能。

更多內容關注 Serverless 微信公眾號(ID:serverlessdevs),彙集 Serverless 技術最全內容,定期舉辦 Serverless 活動、直播,使用者最佳實踐。

相關文章