Serverless與微服務探索(一)- 如何用serverless實踐Spring boot專案

Woody發表於2021-11-12

前言

隨著技術的發展,我們有越來越多的選擇來實現我們的業務邏輯。Serverless作為時下前沿的技術,是不是也可以探索一下微服務架構的新可能性?

這篇文章就是總結近段時間以來,我探索的用serverless落地SpringBoot微服務專案的一些成果。

什麼是Serverless

什麼是微服務和什麼是springBoot已經不需要我講解了。

那什麼是Serverless呢?

根據CNCF的定義,Serverless(無伺服器)是指構建和執行不需要伺服器管理的應用程式的概念。

Serverless並不是沒有伺服器就能進行計算,而是指對於開發者或者公司來說,無需瞭解和管理底層伺服器,就能進行計算。

通俗一點講,Serverless就是封裝了底層計算資源,你只需要提供函式,就可以執行了。

截圖2021-09-29 14.50.07.png

這裡還要提到一個概念,就是FaaS(Function as a Service),函式即服務。我們通常執行在Serverless上的邏輯是函式級別的粒度。

因此對於拆分粒度控制很合理的微服務,是非常適合使用serverless的。

Serverless對於微服務的價值

  1. 每個微服務API被呼叫的頻率不一樣,可以利用Serverless精準管理成本和彈性。
  2. 不用擔心一個API呼叫量大而需要擴容整個服務。Serverless可以自動擴縮容。
  3. 不需要去運維每個服務背後部署多少個容器,多少個伺服器,不用做負載均衡。
  4. 遮蔽了K8S等容器編排的複雜學習成本。
  5. Serverless這種無狀態的特性也非常符合微服務使用Restful API的特性。

初步實踐

首先,需要準備一個SpringBoot專案,可以通過start.spring.io快速建立一個。

在業務開發上,Serverless和傳統的微服務開發並沒有任何不同。所以我就快速寫了一個todo後端服務,完成了增刪改查功能。

示例程式碼在這裡

那麼使用Serverless真正有差異的地方在哪裡呢?

如果只是簡單的想要部署單個服務,那麼主要差異在於兩個方面:

  1. 部署方式
  2. 啟動方式

部署方式

由於我們摸不到伺服器了,所以部署方式的變化是很大的。

傳統的微服務部署,通常是直接部署到虛擬機器上執行,或者用K8S做容器化的排程。

傳統的部署關係大致如下圖。
截圖2021-11-11 17.19.48.png

如果使用serverless通常要求我們的微服務拆分粒度更細,才能做到FaaS。
所以使用Serverless部署微服務的關係大致如下圖。
截圖2021-11-11 17.27.47.png

Serverless只需要提供程式碼就可以了,因為serverless自帶執行環境,因此serverless部署微服務通常有兩種方式:

  1. 程式碼包上傳部署
  2. 映象部署

第一種方式和傳統部署相比是差異最大的。它需要我們將寫好的程式碼打包上傳。並且需要指定一個入口函式或者指定監聽埠。

第二個種方式和傳統的方式相比幾乎不變,都是把做好的映象上傳到我們的映象倉庫。然後在serverless平臺部署的時候選擇對應的映象。

啟動方式

因為serverless是使用的時候才會建立對應的例項,不使用的時候就會銷燬例項,體現了serverless按量計費的特點。

所以serverless在第一次呼叫的時候存在一個冷啟動的過程。所謂冷啟動就是指需要平臺分配計算資源、載入並啟動程式碼。因此依據不同的執行環境和程式碼可能有不同的冷啟動時間。

而Java作為一種靜態語言,它的啟動速度也一直被人詬病。然而還有更慢的,就是spring的啟動時間,是大家有目共睹的慢。所以,java+spring這種強強聯合造就了樹懶般的啟動速度。就有可能造成首次呼叫服務出現超長的等待時間。

不過,不用擔心,spring已經提供了兩種解決方案來縮短啟動時間。

  1. 一種是SpringFu
  2. 另一種是Spring Native

SpringFu

Spring Fu 是 JaFu (Java DSL) 和 KoFu (Kotlin DSL) 的孵化器,以宣告式方式使用程式碼顯式配置 Spring Boot,由於自動完成,具有很高的可發現性。 它提供快速啟動(比最小 Spring MVC 應用程式上的常規自動配置快 40%)、低記憶體消耗,並且由於其(幾乎)無反射方法非常適合 GraalVM 本機。如果搭配上GraalVM編譯器,應用啟動速度就能直線下降到原先的大約1%。

不過,目前SpringFu還處於特別早期的階段,使用過程中問題也比較多。另外,使用SpringFu會有較大的程式碼改造成本,因為它幹掉了所有的annotation,所以這次我沒有使用SpringFu的方式。

Spring Native

Spring Native 為使用 GraalVM native-image編譯器將 Spring 應用程式編譯為native可執行檔案,以提供打包在輕量級容器中的native部署選項。 Spring Native的目標是在這個新平臺上支援幾乎沒有程式碼改造成本的 Spring Boot 應用程式。

因此我選擇了Spring native,因為它不需要改造程式碼,只需要新增一些外掛與依賴就能實現native image。

Native image有幾大好處:

  1. 在構建時會移除未使用的程式碼
  2. classpath 在構建時就已經確定
  3. 沒有類延遲載入:可執行檔案中所有的內容都會在啟動時載入到記憶體中
  4. 在構建時就執行了一些程式碼

基於這些特性,因此它能讓程式的啟動時間大大加快。

關於如何使用它我將在下一篇文章中講解,詳細教程可以檢視這個官方教程。我也是參考這個教程做的。

我就說說我的測試對比結果吧。

我把編譯好的image分別在本地,騰訊雲serverless的雲函式和AWS serverless lambda進行了部署和測試。

規格SpringBoot冷啟動時長SpringNative冷啟動時長
本地16G記憶體Mac1秒79毫秒
騰訊雲Serverless 256M記憶體13秒300毫秒
AWS Serverless 256M記憶體21秒1秒

從測試結果看,SpringNative大大提升了啟動速度。提高serverless的規格還能進一步提升速度。

如果Serverless的冷啟動速度控制到了1秒內,那麼大部分業務都是能接受的。並且也只有首次請求的時候會存在冷啟動的情況,其他請求都和普通的微服務響應時間一樣。

此外,目前各大平臺的Serverless都支援預置例項,也就是在訪問到來之前提前建立例項來減少冷啟動時間。帶來業務上更高的相應時間。

總結

Serverless作為目前先進的技術,它給我們帶來諸多好處。

  1. 自動擴縮容的彈性和併發性
  2. 細粒度的資源分配
  3. 鬆耦合
  4. 免運維

但serverless也不是完美的,當我們嘗試在微服務領域使用它的時候,我們依然能看到它存在很多問題等待解決。

  1. 難以監視和除錯

    1. 這是目前公認的一個痛
  2. 可能會有更多的冷啟動

    1. 當我們拆分微服務為了適應函式粒度時,同時也分散了每個函式的呼叫時間,導致每個函式呼叫頻率變低,帶來更多的冷啟動。
  3. 函式間的互動會更復雜

    1. 由於函式粒度變細,在大型微服務專案中,導致原本就錯綜複雜的微服務會變得更加錯綜複雜。

總結起來就是,想要完全替代傳統的虛擬機器,在微服務這條路上Serverless還有很長的路要走。

下一步

我會繼續探索serverless與微服務的實踐。

後面的文章我會探討下面幾個話題

  • Serverless中的服務間呼叫
  • Serverless中的資料庫訪問
  • Serverless中的服務的註冊與發現
  • Serverless中的服務熔斷與降級
  • Serverless中的服務拆分

相關文章