精讀《Serverless 給前端帶來了什麼》

黃子毅發表於2019-03-11

1. 引言

Serverless 是一種 “無伺服器架構”,讓使用者無需關心程式執行環境、資源及數量,只要將精力 Focus 到業務邏輯上的技術。

現在公司已經實現 DevOps 化,正在向 Serverless 邁進,而為什麼前端要關注 Serverless?

對業務前端同學:

  1. 會改變前後端介面定義規範。
  2. 一定會改變前後端聯調方式,讓前端參與伺服器邏輯開發,甚至 Node Java 混部。
  3. 大大降低 Nodejs 伺服器維護門檻,只要會寫 JS 程式碼就可以維護 Node 服務,而無需學習 DevOps 相關知識。

對一個自由開發者:

  1. 未來伺服器部署更彈性,更省錢。
  2. 部署速度更快,更不易出錯。

前端框架總是帶入後端思維,而 Serverless 則是把前端思維帶入了後端運維。

前端開發者其實是最早享受到 “Serverless” 好處的群體。他們不需要擁有自己的服務,甚至不需要自己的瀏覽器,就可以讓自己的 JS 程式碼均勻、負載均衡的執行在每一個使用者的電腦中。

而每個使用者的瀏覽器,就像現在最時髦,最成熟的 Serverless 叢集,從遠端載入 JS 程式碼開始冷啟動,甚至在冷啟動上也是卓越領先的:利用 JIT 加速讓程式碼實現毫秒級別的冷啟動。不僅如此,瀏覽器還是實現了 BAAS 服務的完美環境,我們可以呼叫任何函式獲取使用者的 Cookie、環境資訊、本地資料庫服務,而無需關心使用者用的是什麼電腦,連線了怎樣的網路,甚至硬碟的大小。

這就是 Serverless 理念。通過 FAAS(函式及服務)與 BAAS(後臺及服務)企圖在服務端製造前端開發者習以為常的開發環境,所以前端開發者應該更能理解 Serverless 帶來的好處。

2. 精讀

FAAS(函式即服務) + BAAS(後臺即服務) 可以稱為一個完整的 Serverless 的實現,除此之外,還有 PASS(平臺即服務)的概念。而通常平臺環境都通過容器技術實現,最終都為了達到 NoOps(無人運維),或者至少 DevOps(開發&運維)。

簡單介紹一下這幾個名詞,防止大家被繞暈:

FAAS - Function as a service

函式即服務,每一個函式都是一個服務,函式可以由任何語言編寫,除此之外不需要關心任何運維細節,比如:計算資源、彈性擴容,而且可以按量計費,且支援事件驅動。業界大雲廠商都支援 FAAS,各自都有一套工作臺、或者視覺化工作流來管理這些函式。

BAAS - Backend as a service

後端及服務,就是整合了許多中介軟體技術,可以無視環境呼叫服務,比如資料即服務(資料庫服務),快取服務等。雖然下面還有很多 XASS,但組成 Serverless 概念的只有 FAAS + BAAS。

PAAS - Platform as a service

平臺即服務,使用者只要上傳原始碼就可以自動持續整合並享受高可用服務,如果速度足夠快,可以認為是類似 Serverless。但隨著以 Docker 為代表的容器技術興起,以容器為粒度的 PASS 部署逐漸成為主流,是最常用的應用部署方式。比如中介軟體、資料庫、作業系統等。

DAAS - Data as a service

資料即服務,將資料採集、治理、聚合、服務打包起來提供出去。DASS 服務可以應用 Serverless 的架構。

IAAS - Infrastructure as a Service

基礎設施即服務,比如計算機儲存、網路、伺服器等基建設施以服務的方式提供。

SAAS - Software as a Service

軟體即服務,比如 ERP、CRM、郵箱服務等,以軟體為粒度提供服務。

容器

容器就是隔離了物理環境的虛擬程式執行環境,而且環境可被描述、遷移。比較熱門的容器技術是 Docker。

隨著容器數量增多,就出現了管理容器叢集的技術,比較有名的容器編排平臺是 Kubernetes。容器技術是 Serverless 架構實現的一種選擇,也是實現的基礎。

NoOps

就是無人運維,比較理想主義,也許要藉助 AI 的能力才能實現完全無人運維。

無人運維不代表 Serverless,Serverless 可能也需要人運維(至少現在),只是開發者不再需要關心環境。

DevOps

筆者覺得可以理解為 “開發即運維”,畢竟出了事情,開發要被問責,而一個成熟的 DevOps 體系可以讓更多的開發者承擔 OP 的職責,或者與 OP 更密切的合作。


回到 Serverless,未來後端開發的體驗可能與前端相似:不需要關心程式碼執行在哪臺伺服器(瀏覽器),無需關心伺服器環境(瀏覽器版本)、不用擔心負載均衡(前端從未擔心過)、中介軟體服務隨時呼叫(LocalStorage、Service Worker)

前端同學對 Serverless 應該尤為激動。就拿筆者親身經歷舉例吧。

從做一款遊戲說起

筆者非常迷戀養成類遊戲,養成遊戲最常見的就是資源建造、收集,或者掛機時計算資源的 讀秒規則。筆者在開發遊戲的時候,最初是將客戶端程式碼與服務端程式碼完全分成兩套實現的:

// ... UI 部分,畫出一個倒數計時伐木場建造進度條
const currentTime = await requestBuildingProcess();
const leftTime = new Date().getTime() - currentTime;
// ... 繼續倒數計時讀條
// 讀條完畢後,每小時木頭產量 + 100,更新到客戶端計時器
store.woodIncrement += 100;

為了遊戲體驗,使用者可以在不重新整理瀏覽器的情況下,看到伐木場建造進度的讀條,以及 嘭 一下建造完畢,並且發現每秒鐘多獲得了 100 點木材!但是當伐木場 建造完成前、完成時、完成後的任意時間點重新整理瀏覽器,都要保持邏輯的統一,而且資料需要在後端離線計算。 此時就要寫後端程式碼了:

// 每次登陸時,校驗當前登陸
const currentTime = new Date().getTime()
// 獲取伐木場當前狀態
if ( /* 建造中 */) {
  // 返回給客戶端當前時間
  const leftTime = building.startTime - currentTime
  res.body = leftTime
} else {
  // 建造完畢
  store.woodIncrement += 100
}

很快,建築的種類多了起來,不同的狀態、等級產量都不同,前後端分開維護成本會越來越大,我們需要做配置同步。

配置同步

為了做前後端配置同步,可以將配置單獨託管起來前後端共用,比如新建一個配置檔案,專門儲存遊戲資訊:

export const buildings = {
  wood: {
    name: "..",
    maxLevel: 100,
    increamentPerLevel: 50,
    initIncreament: 100
  }
  /* .. and so on .. */
};

這雖然複用了配置,但前後端都有一些共同的邏輯可以複用,比如 根據建築建造時間判斷建築狀態,判斷 N 秒後建築的產量等等。 而 Serverless 帶來了進一步優化的空間。

在 Serverless 環境下做遊戲

試想一下,可以在伺服器以函式粒度執行程式碼,我們可以這樣抽象遊戲邏輯:

// 根據建築建造時間判斷建築狀態
export const getBuildingStatusByTime = (instanceId: number, time: number) => {
  /**/
};

// 判斷建築生產量
export const getBuildingProduction = (instanceId: number, lastTime: number) => {
  const status = getBuildingStatusByTime(instanceId, new Date().getTime());
  switch (status) {
    case "building":
      return 0;
    case "finished":
      // 根據 (當前時間 - 上次開啟時間)* 每秒產量得到總產量
      return; /**/
  }
};

// 前端 UI 層,每隔一秒呼叫一次 getBuildingProduction 函式,及時更新生產資料
// 前端入口函式
export const frontendMain = () => {
  /**/
};

// 後端 根據每次開啟時間,呼叫一次 getBuildingProduction 函式併入庫
// 後端入口函式
export const backendMain = () => {
  /**/
};

利用 PASS 服務,將前後端邏輯寫在一起,將 getBuildingProduction 函式片段上傳至 FAAS 服務,這樣就可以同時共享前後端邏輯了!

在資料夾檢視下,可以做如下結構規劃:

.
├── client    # 前端入口
├── server    # 後端入口
├── common    # 共享工具函式,可以包含 80% 的通用遊戲邏輯

也許有人會問:前後端共享程式碼不止有 Serverless 才能做到。

的確如此,如果程式碼抽象足夠好,有成熟的工程方案支援,是可以將一份程式碼分別匯出到瀏覽器與伺服器的。但 Serverless 基於函式粒度功能更契合前後端複用程式碼的理念,它的出現可能會推動更廣泛的前後端程式碼複用,這雖然不是新發明,但足夠稱為一個偉大的改變。

前後端的視角

對於前端開發者,會發現後臺服務變簡單了。對於後端開發者,發現服務做厚了,面臨的挑戰更多了。

更簡單的後臺服務

傳統 ECS 伺服器在租賃時,CentOS 與 AliyunOS 的環境選擇就足夠讓人煩惱。對個人開發者而言,我們要搭建一個完整的持續整合服務是很困難的,而且面臨的選擇很多,讓人眼花繚亂:

  • 可以在伺服器安裝資料庫等服務,本地直聯伺服器的資料庫進行開發。
  • 可以本地安裝 Docker 連線本地資料庫服務,將環境打包成映象整體部署到伺服器。
  • 將前後端程式碼分離,前端程式碼在本地開發,服務端程式碼在伺服器開發。

甚至伺服器的穩定性,需要 PM2 等工具進行管理。當伺服器面臨攻擊、重啟、磁碟故障時,開啟復雜的工作臺或登陸 Shell 後一通操作才能恢復。這怎麼讓人專心把精力放在要做的事情上呢?

Serverless 解決了這個問題,因為我們要上傳的只是一個程式碼片段,不再需要面對伺服器、系統環境、資源等環境問題,外部服務也有封裝好的 BAAS 體系支援。

實際上在 Serverless 出來之前,就有許多後端團隊利用 FAAS 理念簡化開發流程。

為了減少寫後端業務邏輯時,環境、部署問題的干擾,許多團隊會將業務邏輯抽象成一個個區塊(Block),對應到程式碼片段或 Blockly,這些區塊可以獨立維護、釋出,最後將這些程式碼片段注入到主程式中,或動態載入。如果習慣了這種開發方式,那也更容易接受 Serverless。

更厚的後臺服務

站在後臺角度,事情就變得比較複雜了。相對於提供簡單的伺服器和容器,現在要對使用者遮蔽執行環境,將服務做得更厚。

筆者通過一些文章瞭解到,Serverless 的推行還面臨著如下一些挑戰:

  • Serverless 各廠商實現種類很多,想讓業務做到多雲部署,需要抹平差異。
  • 成熟的 PASS 服務其實是偽 Serverless,後續該如何標準化。
  • FAAS 冷啟動需要重新載入程式碼、動態分配資源,導致冷啟動速度很慢,除了預熱,還需要經濟的優化方式。
  • 對於高併發(比如雙 11 秒殺)場景的應用,無需容量評估是很危險的事情,但如果真能做到完全彈性化,就省去了煩惱的容量評估。
  • 存量應用如何遷移。業界的 Serverless 服務廠商大部分都沒有解決存量應用遷移的問題。
  • Serverless 的特性導致了無狀態,而複雜的網際網路應用都是有狀態的,因此挑戰在不改變開發習慣的情況下支援狀態。

所幸的是,這些問題都已經在積極處理中,而且不少有了已經落地的解決方案。

Serverless 給後臺帶來的好處遠比面臨的挑戰多:

  • 推進前後端一體化。進一步降低 Node 寫服務端程式碼的門檻,免除應用運營的學習成本。筆者曾經遇到過自己申請的資料庫服務被遷移到其他機房而導致的應用服務中斷,以後再也不需要擔心了,因為資料庫作為 BAAS 服務,是不需要關心在哪部署,是否跨機房,以及如何做遷移的。
  • 提高資源利用效率。杜絕應用獨佔資源,換成按需載入必然能減少不必要的資源消耗,而且將服務均攤到叢集的每一臺機器,拉平叢集的 CPU 水位。
  • 降低雲平臺使用門檻。無需運維,靈活擴充,按價值服務,高可用,這些能力在吸引更多客戶的同時,完全按需計費的特性也減少了使用者開銷,達到雙贏。

利用 Serverless 嘗試服務開放

筆者在公司負責一個大型 BI 分析平臺建設,BI 分析平臺的底層能力之一就是視覺化搭建。

那麼視覺化搭建能力該如何開放呢?現在比較容易做到的是元件開放,畢竟前端可以與後端設計相對解耦,利用 AMD 載入體系也比較成熟。

現在遇到的一個挑戰就是後端能力開放,因為當對取數能力有定製要求時,可能需要定製後端資料處理的邏輯。目前能做到的是利用 maven3、jdk7 搭建本地開發環境測試,如果想上線,還需要後端同學的協助。

如果後端搭建一個特有的 Serverless BAAS 服務,那麼就可以像前端元件一樣進行線上 Coding,除錯,甚至灰度釋出進行預發測試。現在前端雲端開發已經有了不少成熟的探索,Serverless 可以統一前後端程式碼在雲端開發的體驗,而不需要關心環境。

Serverless 應用架構設計

看了一些 Serverless 應用架構圖,發現大部分業務都可以套用這樣一張架構圖:

將業務函式抽象成一個個 FAAS 函式,將資料庫、快取、加速等服務抽象成 BAAS 服務。

上層提供 Restful 或事件觸發機制呼叫,對應到不同的端(PC、移動端)。

想要擴充平臺能力,只要在端上做開放(元件接入)與 FAAS 服務做開放(後端接入)即可。

收益與挑戰

Serverless 帶來了的收益與挑戰並存,本文站在前端角度聊一聊。

收益一:前端更 Focus 在前端體驗技術,而不需要具備太多應用管理知識。

最近看了很多前端前輩寫的總結文,最大的體會就是回憶 “前端在這幾年到底起到了什麼作用”。我們往往會誇大自己的存在感,其實前端存在的意義就是解決人機互動問題,大部分場景下,都是一種景上添花的作用,而不是必須。

回憶你最自豪的工作經歷,可能是掌握了 Node 應用的運維知識、前端工程體系建設、研發效能優化、標準規範制定等,但真正對業務起效的部分,恰恰是你覺得寫得最不值得一提的業務程式碼。前端花了太多的時間在周邊技術上,而減少了很多對業務、互動的思考。

即便是大公司,也難以招到既熟練使用 Nodejs,又具備豐富運維知識的人,同時還要求他前端技術精湛,對業務理解深刻,魚和熊掌幾乎不可兼得。

Serverless 可以有效解決這個問題,前端同學只需要會寫 JS 程式碼而無需掌握任何運維知識,就可以快速實現自己的整套想法。

誠然,瞭解服務端知識是有必要的,但站在合理分工的角度,前端就應該 focus 在前端技術上。前端的核心競爭力或者帶來的業務價值,並不會隨著瞭解多一些運維知識而得到補充,相反,這會吞噬掉我們本可以帶來更多業務價值的時間。

語言的進化、瀏覽器的進化、伺服器的進化,都是從複雜到簡單,底層到封裝的過程,而 serverless 是後端 + 運維作為一個整體的進一步封裝的過程。

收益二:邏輯編排帶來的程式碼高度複用、可維護,擴充 雲+端 的能力。

雲+端 是前端開發的下個形態,提供強大的雲編碼能力,或者通過外掛將端打造為類似雲的開發環境。其最大好處就是遮蔽前端開發環境細節,理念與 Serverless 類似。

之前有不少團隊嘗試過利用 Graphsql 讓介面 “更有彈性”,而 Serverless 則是更徹底的方案。

我自己的團隊就嘗試過 Graphsql 方案,但由於業務非常複雜,難以用標準的模型描述所有場景的需求,因此不適合使用 Graphsql。恰恰是一套基於 Blockly 的視覺化後端開發平臺堅持了下來,而且取得了驚人的開發提效。這套 Blockly 通用化抽象後幾乎可以由 Serverless 來代替。所以 Serverless 可以解決複雜場景下後端研發提效的問題。

Serverless 在融合了雲端開發後,就可以通過邏輯編排進一步視覺化調整函式執行順序、依賴關係。

筆者之前在百度廣告資料處理團隊使用過這種平臺計算離線日誌,每個 MapReduce 計算節點經過視覺化後,就可以輕鬆看出故障時哪個節點在阻塞,還可以看到最長執行鏈路,併為每個節點重新分配執行權重。即便邏輯編排不能解決開發的所有痛點,但在某個具體業務場景下一定可以大有作為。

挑戰一:Serverless 可以完全取消前端轉後端的門檻?

前端同學寫 Node 程式碼最容易犯的毛病就是記憶體溢位。

瀏覽器 + Tab 天然是一個用完即關的場景,UI 元件與邏輯建立與銷燬也非常頻繁,因此前端同學很少,也不太需要關心 GC 問題。而 GC 在後端開發場景中是一個早已養成的習慣,因此 Nodejs 程式快取溢位是大家最關注的問題。

Serverless 應用是動態載入,長時間不用就會釋放的,因此一般來說不需要太擔心 GC 的問題,就算記憶體溢位,在記憶體被佔滿前可能已經程式被釋放,或者被監測到異常強制 Kill 掉。

但畢竟 FAAS 函式的載入與釋放完全是由雲端控制的,一個常用的函式長時間不解除安裝也是有可能的,因此 FAAS 函式還是要注意控制副作用。

所以 Serverless 雖然抹平了運維環境,但服務端基本知識還需要了解,必須意識到程式碼跑在前端還是後端。

挑戰二:效能問題

Serverless 的冷啟動會導致效能問題,而讓業務方主動關心程式的執行頻率或者效能要求,再開啟預熱服務又重新將研發拖入了運維的深淵中。

即便是業界最成熟的亞馬遜 Serverless 雲服務,也無法做到業務完全不關心呼叫頻率,就可以輕鬆應付秒殺場景。

因此目前 Serverless 可能更適合結合合適的場景使用,而不是任何應用都強行套用 Serverless。

雖然可以通過定期執行 FAAS 服務來保證程式一直 Online,但筆者認為這還是違背了 Serverless 的理念。

挑戰三:如何保證程式碼可遷移性

有一張很經典的 Serverless 定位描述圖:

TB1i7PULkzoK1RjSZFlXXai4VXa-804-1184.png =00x300

網路、儲存、服務、虛擬家、作業系統、中介軟體、執行時、資料都不需要關心了,甚至連應用層都只需要關心其中函式部分,而不需要關心其他比如啟動、銷燬部分。

前面總拿這點當優勢,但也可以反過來認為是個劣勢。 當你的程式碼完全依賴某個公有云環境後,你就失去了整體環境的掌控力,甚至程式碼都只能在特定的雲平臺才能執行。

不同雲平臺提供的 BAAS 服務規範可能不同,FAAS 的入口、執行方式也可能不同,想要採用多雲部署就必須克服這個問題。

現在許多 Serverless 平臺都在考慮做標準化,但同時也有一些自下而上的工具庫抹平一些差異,比如 Serverless Framework 等。

而我們寫 FAAS 函式時,也儘量將與平臺繫結的入口函式寫得輕一些,將真正的入口放在通用的比如 main 函式中。

3. 總結

Serverless 的價值遠比挑戰大,其理念可以切實解決許多研發效能問題。

但目前 Serverless 發展階段仍處於早期,國內的 Serverless 也處於嘗試階段,而且執行環境存在諸多限制,也就是並沒有完全實現 Serverless 的美好理念,因此如果什麼都往上套一定會踩坑。

可能在 3-5 年後,這些坑會被填平,那麼你是選擇加入填坑大軍,還是選一個合適的場景使用 Serverless 呢?

討論地址是:精讀《Serverless 給前端帶來了什麼》 · Issue #135 · dt-fe/weekly

如果你想參與討論,請點選這裡,每週都有新的主題,週末或週一釋出。前端精讀 - 幫你篩選靠譜的內容。

相關文章