Nexus協議,閒魚一體化開發的幕後玩家

背景

Serverless是這幾年興起的一個概念,它希望開發者更加專注於應用邏輯本身,而不是被瑣碎的基礎設施細節所”綁架“。而FaaS是Serverless的一種比較好的實踐方式。它擁有更加輕量、事件驅動的特點。

閒魚選擇使用Flutter + FaaS體系來實現雲端一體化的開發模式也正是看中了Flutter和FaaS技術本身都是輕量的、面向應用的技術。與一體化本身希望開發者儘可能關注整體的業務邏輯非常契合。

Flutter + FaaS的雲端一體化開發模式已經在閒魚中被使用了一段時間。同事們之前也有過一些文章來介紹一體化開發在閒魚演進和落地的過程。在這些文章中,都提到了 <code>Logic_engine/<code>、<code>Nexus_Framework/<code>等字眼。它們一直默默地在業務開發同學的身後,支撐著一體化的落地和發展。

今天,我們就來介紹一下這個一體化的幕後玩家--- Nexus協議,以及基於它衍生出來的框架和庫。

Nexus協議的來源

一開始說要做Flutter + FaaS一體化開發的時候,我們對”一體化“這三個字的認知相對比較模糊,只是知道端側的同學可以用Dart這門語言來寫FaaS函數,這樣的語言上的一體化。對於FaaS所能做的事,也僅僅停留在前端實施已久的BFF層面。那個階段,對於要做些什麼,還是比較迷茫的。

阿里的同學經常說:你不知道能做些什麼,是因為想得還不夠清楚。

本著這樣的想法,一體化小組經常聚在一起討(liao)論(tian),不管Flutter + FaaS有沒有一體化,反正我們小組先”一體化“了再說。

整個一體化的概念在討論中慢慢變得清晰,首先我們對於一體化進行了定義,它應該是這樣的一個形態:

  • 語言一體化

  • 開發模式與架構一體化

最終達到開發Flutter頁面和開發FaaS無明顯gap,像在開發一整個應用的體驗。

語言一體化

由於Flutter本身是以Dart作為開發語言,那麼我們自然也選擇它作為FaaS的開發語言。閒魚在之前已經實踐過了Dart Server這種開發方式,在Dart runtime、相關開發工具方面有非常深厚的沉澱。組內的同學將這個runtime經過修改之後移植到了集團的FaaS平臺Gaia上。

開發同學不僅可以在端上使用hotreload進行頁面快速調試,同樣可以使用這項功能在FaaS平臺上快速部署與調試,極大得提升了部署和調試的體驗。

開發模式與架構一體化

在語言一體化的基礎上,我們同樣希望開發者在開發Flutter頁面和FaaS函數的時候,有著相同的心智。

在傳統的前後端分離開發模式中,端側的開發與後端開發有著比較明顯的不同,端側通過和後端約定數據結構的方式獲取用於頁面渲染和處理用戶輸入的數據。這種模式下,雙方僅對數據進行了依賴,各自屬於不同的系統。

在一體化的模式下,我們希望開發者能把端側頁面和FaaS函數當成同一個系統來看待。它們應該是一個有機的整體,共同完成一個頁面的功能。在職責上,端側代碼主要處理UI的渲染,FaaS函數主要處理邏輯與副作用。

開發者應該可以像在一個系統內一樣進行相互的調用,就好像你在本地調用一個對象的函數那樣自然。

但顯然,端與FaaS現實中還是屬於兩個系統的,如何能夠做到像調用函數一樣自然呢?它們之間又以什麼樣方式進行觸發呢?

Nexus協議的設計

事件驅動

在常見的客戶端頁面開發過程中,端側邏輯總是圍繞著三個操作在進行,不管代碼多少,寫成什麼樣,這些邏輯代碼最終都會產生:

  • 發起一個網絡請求(remote req)

  • 調用一個公共函數(native api)

  • 修改頁面數據並渲染(state change)

這三個效果。比如頁面的初始化過程,就是典型的 <code>remote req/<code>=><code>state change/<code>=><code>render/<code>的過程。

當然這是精簡之後的流程,由於一個http請求回來後的數據並不能直接作用於頁面state,通常還需要先對數據進行一下處理。

這些動作都會由一個明顯的事件來觸發,通常來說是用戶進行的交互事件,不論是請求、頁面渲染,或者彈出一個Dialog,進行一次頁面間的跳轉,它們都不會自發得進行(否則看上去有些詭異)。

而一個端上的事件,也可能會傳導到FaaS上,來驅動FaaS上的邏輯函數對這個事件進行處理。當我們把端和FaaS看成一個整體的時候,這個事件就是在一個系統中流轉。於是我們總結出了第一張圖:

Nexus协议,闲鱼一体化开发的幕后玩家

邏輯歸一與相互調用

在傳統的開發模型下,頁面邏輯、狀態、展示三者之間的流轉是在端側進行的,後端負責了一部分的邏輯處理(通常這部分邏輯是需要對於各種領域接口進行調用)。

而還有一部分領域數據到UI state的一些轉換邏輯,則是端、後端都會做一部分。這兩部分邏輯分散在兩端,通過某種弱的協議進行連接。

引入了FaaS之後,自然可以把邏輯放到FaaS上實現,那麼請求回來的數據理論上可以直接作用於頁面渲染。

如果我們再進一步,不如直接讓FaaS來指揮端上的UI怎麼做好了。就好像FaaS是一個導演,而端側UI是一個提線木偶,FaaS怎麼說,UI怎麼變。這樣把業務相關的邏輯都搬上FaaS去,端側專注於如何將state渲染到UI上,兩個部分組合成為一個頁面整體,豈不是更加一體化?於是有了第二張圖:

Nexus协议,闲鱼一体化开发的幕后玩家

邏輯歸一到FaaS之後,FaaS已經可以跳過傳統的弱協議,直面端側頁面了。對於後端來說,一個請求可以映射到一個具體的處理函數。我們可以不太嚴謹地說,一直以來,客戶端是有調用後端函數的能力的。那麼既然我們現在想讓FaaS來指揮端上的UI的變化,勢必也要讓FaaS具有調用端側函數的能力。

我們把一次調用抽象為一個 <code>Action/<code>,每一個<code>Action/<code>的背後都有一個特定的函數為它提供真實的邏輯,也就是說,一個特定的<code>Action/<code>,可以用來描述一個特定的函數與函數背後的邏輯代碼,<code>Action/<code>本身就是一個函數簽名。

那麼端側需要提供多少函數給FaaS呢?當邏輯歸一到FaaS上之後,我們會發現端側的大部分實現都圍繞著兩部分進行:

  • UI展現

  • 副作用處理

UI的展現是”純“的,它基本上都可以由一個頁面的state數據來描述,也就是說,大部分情況下,一個state就描述當前UI的狀態。那麼對於端側來說,只需要提供一個state到UI的映射函數,理論上就可以讓FaaS具有更新端側UI的能力。也就是說,假設FaaS函數想要更新頁面,只需要下發一個 <code>state change/<code>的<code>Action/<code>,帶上頁面所需要的所有state數據,就可以達到效果。現實場景中,某些頁面的state數據可能巨大無比,不好直接傳輸。我們做了一個<code>JsonPatch/<code>庫來解決這種場景下的問題,如果FaaS只修改了state中的一部分數據,則可以通過下發patch的方式由端來合成一個新的、完整的state。

對於副作用處理的部分,大部分的副作用都來自於比如Dialog,頁面跳轉等。這類操作是通用的,有共性的,我們同樣使用一類叫做 <code>nativeapi/<code>的<code>Action/<code>來描述,這些<code>Action/<code>與它們背後的處理函數,將面向所有使用了Nexus協議的頁面提供這樣的能力。

這兩類函數的抽象,已經可以cover 80%的頁面需求了,而剩下的20%複雜交互的頁面,我們提供custom類型的 <code>Action/<code>來讓開發者進行自定義。

總結

通過對於一體化的定義,以及拆分了需要的功能之後,Nexus協議就破土而出了。它是一個:基於Action的,提供Client/FaaS系統間調用的協議。

Action的調度者——LogicEngine

我們有了可以用於兩個系統間進行相互調用的協議,相當於我們有了一門語言,這門語言只有我們自己認識,所以還需要一個解釋器來執行它。

LogiceEngine就是這樣的一個執行器。如果我們給它下一個定義,LogicEngine就是一個:基於Nexus的Action協議的,提供Client、FaaS之間相互調用能力的庫

Engine本身不提供任何具體的邏輯能力,所有的邏輯能力都需要通過函數的形式註冊到Engine中,並綁定到一個具體類型的 <code>Action/<code>上去。

所以Engine的設計相對明確:

  • 對外,它提供函數註冊和基於消息(action)執行函數調用功能

  • 對內,進行消息解析、函數匹配和執行上下文管理

開發者通過 <code>post/<code>函數來發出一個<code>Action/<code>,相當於通過Engine調用了一個函數,這個函數可能在本地,也可能在FaaS上。這並不是開發者需要關心的內容。甚至於,這個調用會產生什麼影響,也不是當前調用者所需要關心的。

因為調用的發起者實際只是發出了自己的一個意圖,比如在實踐中,我們會在用戶按下"下單"按鈕的時候提交一個意圖(Action)。

這個意圖最終會產生什麼樣的UI變化,FaaS會通過一個 <code>state change/<code>或者<code>nativeapi/<code>形式的<code>Action/<code>直接調用到具體的實現函數去。

而端側註冊在Engine的函數不會很多,前面有提到過,大部分UI編程中的邏輯,都可以被歸納為三類。所以我們大多數時候只需要註冊三種固定類型的處理函數就可以了。

展望

有了Nexus協議、三類通用處理函數的抽象和LogicEngine, <code>意圖/<code>=><code>作用/<code>中間過程就可以變得透明。但這些遠遠不夠。

後續我們還希望對協議進行升級,從現有的json提升到一個更加類型安全的協議上。我們也希望有一個IDL工具,可以自動地將面向 <code>Action/<code>調用轉換成面向接口調用,讓開發者有更好的調用體驗。我們還希望改變現有單向的<code>請求/<code>=><code>應答/<code>模型,讓FaaS可以自由地調用端側函數,再次突破兩個系統之間的gap,變得更加一體化。

閒魚團隊是Flutter+Dart FaaS前後端一體化新技術的行業領軍者,就是現在!客戶端/服務端java/架構/前端/質量工程師面向社會招聘,base杭州阿里巴巴西溪園區,一起做有創想空間的社區產品、做深度頂級的開源項目,一起拓展技術邊界成就極致!

*投餵簡歷給小閒魚→guicai.gxy@alibaba-inc.com

Nexus协议,闲鱼一体化开发的幕后玩家

開源項目、峰會直擊、關鍵洞察、深度解讀

請認準閒魚技術


分享到:


相關文章: