Nexus协议,闲鱼一体化开发的幕后玩家
Serverless是這幾年興起的一個(gè)概念,Serverless可以幫助開發(fā)者減輕甚至擺脫傳統(tǒng)后端應(yīng)用開發(fā)所需要的服務(wù)器設(shè)備的設(shè)置和運(yùn)維工作,并以服務(wù)接口的方式為開發(fā)者提供所需要的功能。它希望開發(fā)者更加專注于應(yīng)用邏輯本身,而不是被瑣碎的基礎(chǔ)設(shè)施細(xì)節(jié)所”綁架“。
而FaaS是Serverless的一種比較好的實(shí)踐方式。自從亞馬遜的AWS在14年推出Lambada之后,FaaS這種后端發(fā)開方式迅速被大家接受并應(yīng)用。它擁有更加輕量、事件驅(qū)動(dòng)的特點(diǎn)。
閑魚選擇使用Flutter + FaaS體系來(lái)實(shí)現(xiàn)云端一體化的開發(fā)模式也正是看中了Flutter和FaaS技術(shù)本身都是輕量的、面向應(yīng)用的技術(shù)。與一體化本身希望開發(fā)者盡可能關(guān)注整體的業(yè)務(wù)邏輯非常契合。
Flutter + FaaS的云端一體化開發(fā)模式已經(jīng)在閑魚中被使用了一段時(shí)間。同事們之前也有過(guò)一些文章來(lái)介紹一體化開發(fā)在閑魚演進(jìn)和落地的過(guò)程。在這些文章中,都提到了Logic_engine、Nexus_Framework等字眼。它們一直默默得在業(yè)務(wù)開發(fā)同學(xué)的身后,支撐著一體化的落地和發(fā)展。
今天,我們就來(lái)介紹一下這個(gè)一體化的幕后推手--- Nexus協(xié)議,以及基于它衍生出來(lái)的框架和庫(kù)。
Nexus協(xié)議的由來(lái)
一開始說(shuō)要做Flutter + FaaS一體化開發(fā)的時(shí)候,我們對(duì)”一體化“這三個(gè)字的認(rèn)知相對(duì)比較模糊,只是知道端側(cè)的同學(xué)可以用Dart這門語(yǔ)言來(lái)寫FaaS函數(shù),這樣的語(yǔ)言上的一體化。對(duì)于FaaS所能做的事,也僅僅停留在前端實(shí)施已久的BFF層面。那個(gè)階段,對(duì)于要做些什么,還是比較迷茫的。
阿里的同學(xué)經(jīng)常說(shuō):
你不知道能做些什么,是因?yàn)橄氲眠€不夠清楚
本著這樣的想法,一體化小組經(jīng)常聚在一起討(liao)論(tian),不管Flutter + FaaS有沒(méi)有一體化,反正我們小組先”一體化“了再說(shuō)。
整個(gè)一體化的概念在討論中慢慢變得清晰,首現(xiàn)我們對(duì)于一體化進(jìn)行了定義,它應(yīng)該是這樣的一個(gè)形態(tài):
最終達(dá)到開發(fā)Flutter頁(yè)面和開發(fā)FaaS無(wú)明顯gap,像在開發(fā)一整個(gè)應(yīng)用的體驗(yàn)。
語(yǔ)言一體化
由于Flutter本身是以Dart作為開發(fā)語(yǔ)言,那么我們自然也選擇它作為FaaS的開發(fā)語(yǔ)言。閑魚在之前已經(jīng)實(shí)踐過(guò)了Dart Server這種開發(fā)方式,在Dart runtime、相關(guān)開發(fā)工具方面有非常深厚的沉淀。組內(nèi)的同學(xué)將這個(gè)runtime經(jīng)過(guò)修改之后移植到了集團(tuán)的FaaS平臺(tái)Gaia上。
開發(fā)同學(xué)不僅可以在端上使用hotreload進(jìn)行頁(yè)面快速調(diào)試,同樣可以使用這項(xiàng)功能在FaaS平臺(tái)上快速部署與調(diào)試,極大得提升了部署和調(diào)試的體驗(yàn)。
開發(fā)模式與架構(gòu)一體化
在語(yǔ)言一體化的基礎(chǔ)上,我們同樣希望開發(fā)者在開發(fā)Flutter頁(yè)面和FaaS函數(shù)的時(shí)候,有著相同的心智。
在傳統(tǒng)的前后端分離開發(fā)模式中,端側(cè)的開發(fā)與后端開發(fā)有著比較明顯的不同,端側(cè)通過(guò)和后端約定數(shù)據(jù)結(jié)構(gòu)的方式獲取用于頁(yè)面渲染和處理用戶輸入的數(shù)據(jù)。這種模式下,雙方僅對(duì)數(shù)據(jù)進(jìn)行了依賴,各自屬于不同的系統(tǒng)。
在一體化的模式下,我們希望開發(fā)者能把端側(cè)頁(yè)面和FaaS函數(shù)當(dāng)成同一個(gè)系統(tǒng)來(lái)看待。它們應(yīng)該是一個(gè)有機(jī)的整體,共同完成一個(gè)頁(yè)面的功能。在職責(zé)上,端側(cè)代碼主要處理UI的渲染,FaaS函數(shù)主要處理邏輯與副作用。
開發(fā)者應(yīng)該可以像在一個(gè)系統(tǒng)內(nèi)一樣進(jìn)行相互的調(diào)用,就好像你在本地調(diào)用一個(gè)對(duì)象的函數(shù)那樣自然。
但顯然,端與FaaS現(xiàn)實(shí)中還是屬于兩個(gè)系統(tǒng)的,如何能夠做到像調(diào)用函數(shù)一樣自然呢?它們之間又以什么樣方式進(jìn)行觸發(fā)呢?
事件驅(qū)動(dòng)
在常見的客戶端頁(yè)面開發(fā)過(guò)程中,端側(cè)邏輯總是圍繞著三個(gè)操作在進(jìn)行,不管代碼多少,寫成什么樣,這些邏輯代碼最終都會(huì)產(chǎn)生:
這樣的三個(gè)效果。
比如頁(yè)面的初始化過(guò)程,就是典型的?remote req=>state change=>render的過(guò)程。
當(dāng)然這是精簡(jiǎn)之后的流程,由于一個(gè)http請(qǐng)求回來(lái)后的數(shù)據(jù)并不能直接作用于頁(yè)面state,通常還需要先對(duì)數(shù)據(jù)進(jìn)行一下處理。
這些動(dòng)作都會(huì)由一個(gè)明顯的事件來(lái)觸發(fā),通常來(lái)說(shuō)是用戶進(jìn)行的交互事件,不論是請(qǐng)求、頁(yè)面渲染,或者彈出一個(gè)Dialog,進(jìn)行一次頁(yè)面間的跳轉(zhuǎn),它們都不會(huì)自發(fā)得進(jìn)行(否則看上去有些詭異)。
而一個(gè)端上的事件,也可能會(huì)傳導(dǎo)到FaaS上,來(lái)驅(qū)動(dòng)FaaS上的邏輯函數(shù)對(duì)這個(gè)事件進(jìn)行處理。當(dāng)我們把端和FaaS看成一個(gè)整體的時(shí)候,這個(gè)事件就是在一個(gè)系統(tǒng)中流轉(zhuǎn)。
于是我們總結(jié)出了第一張圖:
邏輯歸一與互相調(diào)用
在傳統(tǒng)的開發(fā)模型下,頁(yè)面邏輯、狀態(tài)、展示三者之間的流轉(zhuǎn)是在端側(cè)進(jìn)行的,后端負(fù)責(zé)了一部分的邏輯處理(通常這部分邏輯是需要對(duì)于各種領(lǐng)域接口進(jìn)行調(diào)用)。
而還有一部分領(lǐng)域數(shù)據(jù)到UI state的一些轉(zhuǎn)換邏輯,則是端、后端都會(huì)做一部分。這兩部分邏輯分散在兩端,通過(guò)某種弱的協(xié)議進(jìn)行連接。
引入了FaaS之后,自然可以把邏輯放到FaaS上實(shí)現(xiàn),那么請(qǐng)求回來(lái)的數(shù)據(jù)理論上可以直接作用于頁(yè)面渲染。
如果我們?cè)龠M(jìn)一步,不如直接讓FaaS來(lái)指揮端上的UI怎么做好了。就好像FaaS是一個(gè)導(dǎo)演,而端側(cè)UI是一個(gè)提線木偶,FaaS怎么說(shuō),UI怎么變。這樣把業(yè)務(wù)相關(guān)的邏輯都搬上FaaS去,端側(cè)專注于如何將state渲染到UI上,兩個(gè)部分組合成為一個(gè)頁(yè)面整體,豈不是更加一體化?
于是我們有了第二張圖:
邏輯歸一到FaaS之后,FaaS已經(jīng)可以跳過(guò)傳統(tǒng)的弱協(xié)議,直面端側(cè)頁(yè)面了。對(duì)于后端來(lái)說(shuō),一個(gè)請(qǐng)求可以映射到一個(gè)具體的處理函數(shù)。我們可以不太嚴(yán)謹(jǐn)?shù)谜f(shuō),一直以來(lái),客戶端是有調(diào)用后端函數(shù)的能力的。那么既然我們現(xiàn)在想讓FaaS來(lái)指揮端上的UI的變化,勢(shì)必也要讓FaaS具有調(diào)用端側(cè)函數(shù)的能力。
我們把一次調(diào)用抽象為一個(gè)Action,每一個(gè)Action的背后都有一個(gè)特定的函數(shù)為它提供真實(shí)的邏輯,也就是說(shuō),一個(gè)特定的Action,可以用來(lái)描述一個(gè)特定的函數(shù)與函數(shù)背后的邏輯代碼,Action本身就是一個(gè)函數(shù)簽名。
那么端側(cè)需要提供多少函數(shù)給FaaS呢?當(dāng)邏輯歸一到FaaS上之后,我們會(huì)發(fā)現(xiàn)端側(cè)的大部分實(shí)現(xiàn)都圍繞著兩部分進(jìn)行:
UI的展現(xiàn)是”純“的,它基本上都可以由一個(gè)頁(yè)面的state數(shù)據(jù)來(lái)描述,也就是說(shuō),大部分情況下,一個(gè)state就描述當(dāng)前UI的狀態(tài)。那么對(duì)于端側(cè)來(lái)說(shuō),只需要提供一個(gè)state到UI的映射函數(shù),理論上就可以讓FaaS具有更新端側(cè)UI的能力。也就是說(shuō),假設(shè)FaaS函數(shù)想要更新頁(yè)面,只需要下發(fā)一個(gè)state change的Action,帶上頁(yè)面所需要的所有state數(shù)據(jù),就可以達(dá)到效果。現(xiàn)實(shí)場(chǎng)景中,某些頁(yè)面的state數(shù)據(jù)可能巨大無(wú)比,不好直接傳輸。我們做了一個(gè)JsonPatch庫(kù)來(lái)解決這種場(chǎng)景下的問(wèn)題,如果FaaS只修改了state中的一部分?jǐn)?shù)據(jù),則可以通過(guò)下發(fā)patch的方式由端來(lái)合成一個(gè)新的、完整的state。
對(duì)于副作用處理的部分,大部分的副作用都來(lái)自于比如Dialog,頁(yè)面跳轉(zhuǎn)等。這類操作是通用的,有共性的,我們同樣使用一類叫做native api的Action來(lái)描述,這些Action與它們背后的處理函數(shù),將面向所有使用了Nexus協(xié)議的頁(yè)面提供這樣的能力。
這兩類函數(shù)的抽象,已經(jīng)可以cover 80%的頁(yè)面需求了,而剩下的20%復(fù)雜交互的頁(yè)面,我們提供custom類型的Action來(lái)讓開發(fā)者進(jìn)行自定義。
總結(jié)
通過(guò)對(duì)于一體化的定義,以及拆分了需要的功能之后,Nexus協(xié)議就破土而出了。它是一個(gè)
基于Action的,提供Client/FaaS系統(tǒng)間調(diào)用的協(xié)議
Action的調(diào)度者-LogicEngine
我們有了可以用于兩個(gè)系統(tǒng)間進(jìn)行相互調(diào)用的協(xié)議,相當(dāng)于我們有了一門語(yǔ)言,這門語(yǔ)言只有我們自己認(rèn)識(shí),所以還需要一個(gè)解釋器來(lái)執(zhí)行它。
LogiceEngine就是這樣的一個(gè)執(zhí)行器。
如果我們給它下一個(gè)定義,LogicEngine就是一個(gè):
基于Nexus的Action協(xié)議的,提供Client、FaaS之間相互調(diào)用能力的庫(kù)
Engine本身不提供任何具體的邏輯能力,所有的邏輯能力都需要通過(guò)函數(shù)的形式注冊(cè)到Engine中,并綁定到一個(gè)具體類型的Action上去。
所以Engine的設(shè)計(jì)相對(duì)明確:
開發(fā)者通過(guò)post函數(shù)來(lái)發(fā)出一個(gè)Action,相當(dāng)于通過(guò)Engine調(diào)用了一個(gè)函數(shù),這個(gè)函數(shù)可能在本地,也可能在FaaS上。這并不是開發(fā)者需要關(guān)心的內(nèi)容。甚至于,這個(gè)調(diào)用會(huì)產(chǎn)生什么影響,也不是當(dāng)前調(diào)用者所需要關(guān)心的。
因?yàn)檎{(diào)用的發(fā)起者實(shí)際只是發(fā)出了自己的一個(gè)意圖,比如在實(shí)踐中,我們會(huì)在用戶按下"下單"按鈕的時(shí)候提交一個(gè)意圖(Action)。
這個(gè)意圖最終會(huì)產(chǎn)生什么樣的UI變化,FaaS會(huì)通過(guò)一個(gè)state change或者native api形式的Action直接調(diào)用到具體的實(shí)現(xiàn)函數(shù)去。
而端側(cè)注冊(cè)在Engine的函數(shù)不會(huì)很多,前面有提到過(guò),大部分UI編程中的邏輯,都可以被歸納為三類。所以我們大多數(shù)時(shí)候只需要注冊(cè)三種固定類型的處理函數(shù)就可以了。
展望
有了Nexus協(xié)議、三類通用處理函數(shù)的抽象和LogicEngine,意圖=>作用中間過(guò)程就可以變得透明。
但這些遠(yuǎn)遠(yuǎn)不夠,
后續(xù)我們還希望對(duì)協(xié)議進(jìn)行升級(jí),從現(xiàn)有的json提升到一個(gè)更加類型安全的協(xié)議上。
我們也希望有一個(gè)IDL工具,可以自動(dòng)得將面向Action調(diào)用轉(zhuǎn)換成面向接口調(diào)用,讓開發(fā)者有更好的調(diào)用體驗(yàn)。
我們還希望改變現(xiàn)有單向的請(qǐng)求=>應(yīng)答模型,讓FaaS可以自由得調(diào)用端側(cè)函數(shù),再次突破兩個(gè)系統(tǒng)之間的gap,變得更加一體化。
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的Nexus协议,闲鱼一体化开发的幕后玩家的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Flink 与 Hive 的磨合期
- 下一篇: 阿里每天究竟要抵御多少攻击