Spring Boot Serverless 實戰-效能調優

語言: CN / TW / HK

SpringBoot 是基於 Java Spring 框架的套件,它預裝了 Spring 的一系列元件,讓開發者只需要很少的配置就可以建立獨立執行的應用程式。在雲原生的世界,有大量的平臺可以執行 SpringBoot 應用,例如虛擬機器,容器等。但其中最有吸引力的,是以 Serverless 的方式執行 SpringBoot 應用。我將通過一系列文章,從架構,部署,監控、效能、安全等5個方面來分析 Serverless 平臺執行 SpringBoot 應用的優劣。為了讓分析更有代表性,我選擇了 github 上 star 數超過 50k 的電商應用 mall 作為示例。這是系列文章的第四篇, 向大家展示如何對 Serverless 應用效能調優。 ​

例項啟動速度優化

在之前的文章實戰教程中,相信大家都感受到 Serverless 的便捷之美,只需上傳程式碼包和映象就能夠輕鬆上線一個彈性高可用的 Web 應用。但是它仍存在首次啟動“冷啟動延時”的問題,Mall 應用例項的啟動大約 30 秒左右,使用者會感受較長時間的冷啟動延時,在這個“即時時代”應用程式響應慢多少會有些瑕不掩瑜。(“冷啟動”是指函式服務於特定呼叫請求時的狀態,當一段時間沒有請求後,Serverless 平臺則會回收函式例項;等到下一次再有請求時,系統會再次實時拉起例項,這個過程稱之為冷啟動。) ​

在優化冷啟動之前,我們先要分析清楚冷啟動各個階段的耗時。首先在函式計算(FC) 控制檯的服務配置介面,開啟鏈路追蹤功能。 ​

對 mall-admin 服務發起請求,成功後檢視 FC 控制檯,我們能夠看到相應的請求資訊。注意關閉“僅檢視函式錯誤”,這樣才會顯示所有請求。指標監控和呼叫鏈路資料收集會存在一定延時,如果沒有顯示,請等待一會再重新整理。找到冷啟動標記的請求,點選 “更多” 下的 “請求詳情”。

呼叫鏈路會顯示冷啟動各個環節的耗時。冷啟動包含以下幾個環節:

  • 程式碼準備(PrepareCode):主要是下載程式碼包或者映象。由於我們已經啟用了映象加速功能,不需要下載全部的映象,因此這一步的延時非常短。
  • 執行時初始化(RuntimeInitialization):從啟動函式開始,到函式計算(FC)系統探測到應用埠就緒為止。這中間包含了應用啟動時間。在命令列執行 s mall-admin logs 檢視相應的日誌時間,我們也能看到 Spring Boot 應用的啟動需要花大量的時間。
  • 應用初始化(Initialization):函式計算提供了 Initializer 介面,使用者可以將一些初始化邏輯放在 initializer 中執行。
  • 呼叫延時(Invocation):處理請求的延時,這個延時非常短。

從上述鏈路追蹤圖來看,例項啟動時間是瓶頸,我們可以採取多種方式來優化。

1.1. 使用預留例項

Java 類應用普遍啟動較慢。應用在初始化時,也需要和很多外部服務互動,耗時較長。這類流程是業務邏輯需要的,很難優化延時。因此函式計算提供了預留例項功能。預留例項的起停由使用者自己控制,沒有請求也會常駐在那,因此不會有冷啟動的問題,當然使用者需要為整個例項的執行付費,即便例項沒有處理任何請求。

在函式計算控制檯,我們可以在“彈性伸縮”頁面為函式設定預留例項。

使用者在控制檯中配置最小和最大例項數。平臺會預留最小例項數目的例項,最大例項是指該函式下例項的上限。使用者也可以設定定時預留和按指標預留的規則。

建立預留規則後,系統就會建立預留例項。當預留例項就緒後,我們再訪問函式就不會有冷啟動。

1.2. 優化例項啟動速度

延遲初始化

在 Spring Boot 2.2 及更高版本中,可以開啟一個全域性延遲初始化標誌。這將提高啟動速度,但代價是第一個請求的延遲時間可能變長,因為需要等待元件首次初始化。

可在 s.yaml 中為相關應用配置以下環境變數

SPRING_MAIN_LAZY_INITIATIALIZATION=true

關閉優化編譯器

預設情況下,JVM 有多個階段的 JIT 編譯。雖然這些階段可以逐漸提高應用的效率,但它們也會增加記憶體使用的開銷,並增加啟動時間。對於短期執行的 Serverless 應用,請考慮關閉此優化,以犧牲長期效率換取更短的啟動時間。

可在 s.yaml 中為相關應用配置以下環境變數:

JAVA_TOOL_OPTIONS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"

s.yaml 中設定環境變數示例:

如下圖所示,對 mall-admin 函式配置環境變數。然後執行 sudo -E s mall-admin deploy 部署。

登入例項檢查環境變數是否配置正確

在控制檯函式詳情頁的請求列表中找到對應的請求,點選更多中的“例項詳情連結”。

在例項詳情頁中點選“登入例項”。

在 shell 介面中執行 echo 命令,檢視對應的環境變數是否設定正確。

注意:對於非預留例項,一段時間沒有請求後,函式計算系統會自動回收例項。此時無法再登入例項(上面的例項詳情頁面中的登入例項按鈕會變灰)。所以請執行呼叫後,在例項被回收之前儘快登入。

2. 配置合理的例項引數

當我們選擇了應用例項規格,比如 2C4G 或者 4C8G,接下來我們希望知道一個例項處理多少請求可以既能充分利用資源又能夠保證效能。當處理的請求超過一個限制後,系統能夠快速彈出例項,保證應用效能平滑。如何度量例項過載有多個維度,例如 qps 超過一定閾值,或者例項 CPU/Memory/Network/Load 等指標超過閾值等等。函式計算使用例項併發度(Instance Concurrency)來作為例項負載的度量和例項伸縮的依據。例項併發度(Instance Concurrency)是指一個例項能同時執行的請求數。例如將例項併發度設定為 20,則意味著一個例項在任意時刻最大能同時執行 20 個請求。 ​

注意:請區分例項併發度和 QPS 的區別。

使用例項併發度來度量負載有如下優勢:

  • 系統能夠迅速統計例項併發度指標值進行擴縮容。CPU/Memory/Network/Load 等例項級別的指標通常是後臺統計,需要花費數十秒的指標統計後才能進行伸縮,難以滿足線上應用的彈性伸縮要求。
  • 在各種條件下,例項併發度指標都能夠穩定的反映系統負載高低。如果以請求延時作為指標,系統難以區分是例項過載導致延時變大,還是下游服務成為瓶頸導致延時變大。例如一個典型的 Web 應用,通常會訪問 MySQL 資料庫。如果資料庫成為瓶頸,請求延時變大,此時擴容不但毫無意義,而且會壓垮資料庫,讓情況更加惡化。QPS 和請求延時相關,也會有上述問題。

例項併發度作為伸縮依據雖然有上述優點,但使用者常常並不知道該設定多大的例項併發度。我推薦按照下述流程確定合理的併發度: ​

  1. 將應用函式的最大例項數設定為1,確保壓測到單個例項的效能。
  2. 使用負載壓測工具對應用進行壓測,檢視 tps 和請求延時等指標
  3. 逐步調大例項併發度,如果效能仍然良好,則繼續調大;如果效能不符合預期,則調小併發度。

總結

本文由部落格群發一文多發等運營工具平臺 OpenWrite 釋出