導讀:前端開發者是最先享受到 「Serverless」 好處的羣體,由於瀏覽器就是一個開箱即用、甚至無需爲計算付費的環境!Serverless 把前端開發體驗帶入了後端,利用 FaaS 與 BaaS 打造一套開箱即用的後端開發環境。本文做者將從前端角度出發,爲你講述 Serverless 帶來的收益及挑戰。
Serverless 是一種 「無服務器架構」,讓用戶無需關心程序運行環境、資源及數量,只要將精力 Focus 到業務邏輯上的技術。前端
如今公司已經實現 DevOps 化,正在向 Serverless 邁進,而爲何前端要關注 Serverless?git
對業務前端同窗:github
對一個自由開發者:數據庫
前端框架老是帶入後端思惟,而 Serverless 則是把前端思惟帶入了後端運維。後端
前端開發者實際上是最先享受到 「Serverless」 好處的羣體。他們不須要擁有本身的服務,甚至不須要本身的瀏覽器,就可讓本身的 JS 代碼均勻、負載均衡的運行在每個用戶的電腦中。瀏覽器
而每一個用戶的瀏覽器,就像如今最時髦、最成熟的 Serverless 集羣,從遠程加載 JS 代碼開始冷啓動,甚至在冷啓動上也是卓越領先的:利用 JIT 加速讓代碼實現毫秒級別的冷啓動。不只如此,瀏覽器還實現了 BaaS 服務的完美環境,咱們能夠調用任何函數獲取用戶的 Cookie、環境信息、本地數據庫服務,而無需關心用戶用的是什麼電腦,鏈接了怎樣的網絡,甚至硬盤的大小。緩存
這就是 Serverless 理念。經過 FaaS(函數即服務)與 BaaS(後臺即服務)企圖在服務端製造前端開發者習覺得常的開發環境,因此前端開發者應該更能理解 Serverless 帶來的好處。前端框架
FaaS(函數即服務) + BaaS(後臺即服務) 能夠稱爲一個完整的 Serverless 的實現。除此以外,還有 PaaS(平臺即服務)的概念。而一般平臺環境都經過容器技術實現,最終都爲了達到 NoOps(無人運維),或者至少 DevOps(開發&運維)。服務器
簡單介紹一下這幾個名詞,防止你們被繞暈:網絡
函數即服務,每個函數都是一個服務,函數能夠由任何語言編寫,除此以外不須要關心任何運維細節,好比:計算資源、彈性擴容,並且能夠按量計費,且支持事件驅動。業界大雲廠商都支持 FaaS,各自都有一套工做臺、或者可視化工做流來管理這些函數。
後端及服務,就是集成了許多中間件技術,能夠無視環境調用服務,好比數據即服務(數據庫服務),緩存服務等。雖然下面還有不少 XAAS
,但組成 Serverless 概念的只有 FaaS + BaaS。
平臺即服務,用戶只要上傳源代碼就能夠自動持續集成並享受高可用服務,若是速度足夠快,能夠認爲是相似 Serverless。但隨着以 Docker 爲表明的容器技術興起,以容器爲粒度的 PaaS 部署逐漸成爲主流,是最經常使用的應用部署方式。好比中間件、數據庫、操做系統等。
數據即服務,將數據採集、治理、聚合、服務打包起來提供出去。DaaS 服務能夠應用 Serverless 的架構。
基礎設施即服務,好比計算機存儲、網絡、服務器等基建設施以服務的方式提供。
軟件即服務,好比 ERP、CRM、郵箱服務等,以軟件爲粒度提供服務。
容器就是隔離了物理環境的虛擬程序執行環境,並且環境可被描述、遷移,比較熱門的容器技術是 Docker。
隨着容器數量增多,就出現了管理容器集羣的技術,比較有名的容器編排平臺是 Kubernetes。容器技術是 Serverless 架構實現的一種選擇,也是實現的基礎。
就是無人運維,比較理想主義,也許要藉助 AI 的能力才能實現徹底無人運維。
無人運維不表明 Serverless,Serverless 可能也須要人運維(至少如今),只是開發者再也不須要關心環境。
筆者以爲能夠理解爲 「開發即運維」,畢竟出了事情,開發要被問責,而一個成熟的 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 帶來了進一步優化的空間。
試想一下,能夠在服務器以函數粒度執行代碼,咱們能夠這樣抽象遊戲邏輯:
// 根據建築建造時間判斷建築狀態 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 = () => { /**/ };
利用 PaaS 服務,將先後端邏輯寫在一塊兒,將 getBuildingProduction
函數片斷上傳至 FaaS 服務,這樣就能夠同時共享先後端邏輯了!
在文件夾視圖下,能夠作以下結構規劃:
. ├── client # 前端入口 ├── server # 後端入口 ├── common # 共享工具函數,能夠包含 80% 的通用遊戲邏輯
也許有人會問:先後端共享代碼不止有 Serverless 才能作到。
的確如此,若是代碼抽象足夠好,有成熟的工程方案支持,是能夠將一份代碼分別導出到瀏覽器與服務器的。但 Serverless 基於函數粒度功能更契合先後端複用代碼的理念,它的出現可能會推進更普遍的先後端代碼複用,這雖然不是新發明,但足夠稱爲一個偉大的改變。
傳統 ECS 服務器在租賃時,CentOS 與 AliyunOS 的環境選擇就足夠讓人煩惱。對我的開發者而言,咱們要搭建一個完整的持續集成服務是很困難的,並且面臨的選擇不少,讓人眼花繚亂:
甚至服務器的穩定性,須要 PM2 等工具進行管理。當服務器面臨攻擊、重啓、磁盤故障時,打開復雜的工做臺或登錄 Shell 後一通操做才能恢復。這怎麼讓人專心把精力放在要作的事情上呢?
Serverless 解決了這個問題,由於咱們要上傳的只是一個代碼片斷,再也不須要面對服務器、系統環境、資源等環境問題,外部服務也有封裝好的 BaaS 體系支持。
實際上在 Serverless 出來以前,就有許多後端團隊利用 FaaS 理念簡化開發流程。
爲了減小寫後端業務邏輯時,環境、部署問題的干擾,許多團隊會將業務邏輯抽象成一個個區塊(Block),對應到代碼片斷或 Blockly,這些區塊能夠獨立維護、發佈,最後將這些代碼片斷注入到主程序中,或動態加載。若是習慣了這種開發方式,那也更容易接受 Serverless。
站在後臺角度,事情就變得比較複雜了。相對於提供簡單的服務器和容器,如今要對用戶屏蔽執行環境,將服務作得更厚。
筆者經過一些文章瞭解到,Serverless 的推行還面臨着以下一些挑戰:
所幸的是,這些問題都已經在積極處理中,並且很多有了已經落地的解決方案。
Serverless 給後臺帶來的好處遠比面臨的挑戰多:
筆者在公司負責一個大型 BI 分析平臺建設,BI 分析平臺的底層能力之一就是可視化搭建。
那麼可視化搭建能力該如何開放呢?如今比較容易作到的是組件開放,畢竟前端能夠與後端設計相對解耦,利用 AMD 加載體系也比較成熟。
如今遇到的一個挑戰就是後端能力開放,由於當對取數能力有定製要求時,可能須要定製後端數據處理的邏輯。目前能作到的是利用 maven三、jdk7 搭建本地開發環境測試,若是想上線,還須要後端同窗的協助。
若是後端搭建一個特有的 Serverless BaaS 服務,那麼就能夠像前端組件同樣進行線上 Coding、調試,甚至灰度發佈進行預發測試。如今前端雲端開發已經有了很多成熟的探索,Serverless 能夠統一先後端代碼在雲端開發的體驗,而不須要關心環境。
看了一些 Serverless 應用架構圖,發現大部分業務均可以套用這樣一張架構圖:
Serverless 帶來的收益與挑戰並存,本文站在前端角度聊一聊。
收益一:前端更 Focus 在前端體驗技術,而不須要具有太多應用管理知識。
最近看了不少前端前輩寫的總結文,最大的體會就是回憶 「前端在這幾年到底起到了什麼做用」。咱們每每會誇大本身的存在感,其實前端存在的意義就是解決人機交互問題,大部分場景下,都是一種錦上添花的做用,而不是必須。
回憶你最自豪的工做經歷,多是掌握了 Node 應用的運維知識、前端工程體系建設、研發效能優化、標準規範制定等,但真正對業務起效的部分,偏偏是你以爲寫得最不值得一提的業務代碼。前端花了太多的時間在周邊技術上,而減小了不少對業務、交互的思考。
即使是大公司,也難以招到既熟練使用 Nodejs,又具有豐富運維知識的人,同時還要求他前端技術精湛,對業務理解深入,魚和熊掌幾乎不可兼得。
Serverless 能夠有效解決這個問題,前端同窗只須要會寫 JS 代碼而無需掌握任何運維知識,就能夠快速實現本身的整套想法。
誠然,瞭解服務端知識是有必要的,但站在合理分工的角度,前端就應該 focus 在前端技術上。前端的核心競爭力或者帶來的業務價值,並不會隨着瞭解多一些運維知識而獲得補充,相反,這會吞噬掉咱們本能夠帶來更多業務價值的時間。
語言的進化、瀏覽器的進化、服務器的進化,都是從複雜到簡單,底層到封裝的過程,而 Serverless 是後端 + 運維做爲一個總體的進一步封裝的過程。
收益二:邏輯編排帶來的代碼高度複用、可維護,拓展 雲+端 的能力。
雲+端 是前端開發的下個形態,提供強大的雲編碼能力,或者經過插件將端打造爲相似雲的開發環境。其最大好處就是屏蔽前端開發環境細節,理念與 Serverless 相似。
以前有很多團隊嘗試過利用 GraphQL 讓接口 「更有彈性」,而 Serverless 則是更完全的方案。
我本身的團隊就嘗試過 GraphQL 方案,但因爲業務很是複雜,難以用標準的模型描述全部場景的需求,所以不適合使用 GraphQL。偏偏是一套基於 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 定位描述圖:
網絡、存儲、服務、虛擬家、操做系統、中間件、運行時、數據都不須要關心了,甚至連應用層都只須要關心其中函數部分,而不須要關心其餘好比啓動、銷燬部分。
前面總拿這點當優點,但也能夠反過來認爲是個劣勢。 當你的代碼徹底依賴某個公有云環境後,你就失去了總體環境的掌控力,甚至代碼都只能在特定的雲平臺才能運行。
不一樣雲平臺提供的 BaaS 服務規範可能不一樣,FaaS 的入口、執行方式也可能不一樣,想要採用多雲部署就必須克服這個問題。
如今許多 Serverless 平臺都在考慮作標準化,但同時也有一些自下而上的工具庫抹平一些差別,好比 Serverless Framework 等。
而咱們寫 FaaS 函數時,也儘可能將與平臺綁定的入口函數寫得輕一些,將真正的入口放在通用的好比 main
函數中。
Serverless 的價值遠大於挑戰,其理念能夠切實解決許多研發效能問題。
但目前 Serverless 發展階段仍處於早期,國內的 Serverless 也處於嘗試階段,並且執行環境存在諸多限制,也就是並無徹底實現 Serverless 的美好理念,所以若是什麼都往上套必定會踩坑。
可能在 3-5 年後,這些坑會被填平,那麼你是選擇加入填坑大軍,仍是選一個合適的場景使用 Serverless 呢?
iPhone 11 Pro、衛衣、T恤等你來抽,立刻來試試手氣 https://www.aliyun.com/1111/2019/m-lottery?utm_content=g_1000083877
本文做者:黃子毅(紫益)
本文爲雲棲社區原創內容,未經容許不得轉載。