融雲 IM 在 Electron 平臺上的設計實踐

融雲RongCloud發表於2022-03-01

Electron 憑藉其相對更低的研發成本投入、強大的跨平臺支援、擁有基數龐大的 Javascript 開發者受眾等優勢,在 PC 端桌面應用軟體研發領域異軍突起。

本文旨在分享融雲 IM 在 Electron 平臺上的桌面端 SDK 產品開發實踐經驗。關注【融雲全球網際網路通訊雲】瞭解更多

融雲 IM 的 Electron 桌面解決方案目標

1. 提供與傳統桌面通訊軟體相匹配的能力支援

相較於 B/S 架構的 Web 頁面應用,融雲期望能夠在 Electron 環境下向開發者提供更為豐富的本地化能力,以及比 Websocket(or Comet)更高效的雙工通訊通道。

藉助這些在瀏覽器環境下不便實現的技術能力,來整體提高使用者對於桌面端產品的使用體驗,將 Electron 作為一個 C/S 架構軟體執行平臺的潛力發揮到最大。

2. Browser 與 Electron 不同執行時程式碼高度複用

由於 Electron 與標準 Web 應用擁有幾乎相同的技術生態,因此多數產品會要求前端程式碼工程兼顧 Browser 與 Electron,也就是說,一套程式碼既要打包為傳統桌面端應用,又可釋出為瀏覽器中執行的 Web 應用。

基於此,作為 PaaS 能力提供的融雲 IM SDK 需要在兩種不同的 Runtime 下做到差異最小化,避免開發者編寫冗餘的平臺相容程式碼。

3. 便於開發者構建多視窗、多程式複雜桌面端應用

Electron 通過對 IPC 能力的封裝為桌面端應用開發提供了較完善的跨程式通訊方案,藉助此能力,開發者構建的桌面端應用也逐漸趨於複雜。

比較典型的如桌面端通訊產品,通常用一個獨立視窗做基礎的 IM 聊天業務,一個視窗做歷史聊天記錄查詢業務;當有音視訊會議業務場景時,還需要再開一個視窗做會議業務;甚至有開發者提出了與每個聊天物件都保持一個獨立聊天視窗的需求(產品形態如 QQ)。

在這種場景下,長連線狀態維持、訊息同步變得異常複雜,原因在於:

若每個程式視窗都維持獨立長連線,難免會出現某一程式連線與其他程式連線狀態不同步;且開發者需在各程式同時維護連線狀態,複雜度較高;同時還會造成服務的併發能力下降。

若僅有單一主視窗進行連線維持,其他視窗通過 IPC 能力將主視窗作為連線代理,則需要在主程式、各渲染程式中維護複雜的跨程式通訊業務程式碼,從而推高專案整體的複雜度。

目前的 Electron 開發者絕大多數來自於 Web 開發者,既有程式設計思維是建立在瀏覽器頁面內單程式單執行緒的應用模型下構建起來的,對於處理此類多程式模型的產品開發缺乏相關的經驗積累。

為降低類似場景的業務實現複雜度,融雲需要在 PaaS 能力層面上解決多程式連線共享、多程式訊息同步問題,讓開發者在既有程式設計思維模式下將每個業務實現的更為順暢。

4. 需同步適配融雲 IM SDK 多個版本

融雲的既有 Web IM SDK 存在多個不同版本,各版本都有不同數量的客戶積累,且各版本 API 介面設計迥異,跨版本升級成本較高。

考慮到使用不同版本的客戶未來將業務向 Electron 遷移的可能性,我們期望通過架構設計的改進來避免既有客戶做過多的整合程式碼修改,在確保既有客戶不因版本升級而流失的前提下降低 Web 研發團隊自身的多版本 SDK 維護成本。

目標落地推進方案

1. 剝離各版本的共同業務與對外差異性 API 定義

融雲的 IM SDK 各版本分別為不同的程式碼倉庫獨立維護,互無干系,這導致所有的功能(包括即將開發的 Electron 桌面解決方案)都可能要在各個版本倉庫上單獨實現,不僅開發成本高,還會導致實現質量無法保證、或程式碼實現不統一,同時也推高了產研後續流程的測試、上線等環節的成本。


(IM SDK 不同版本獨立維護)

基於目標 4 的要求,在既有現狀下繼續開發,意味著需要在兩個版本的基礎上做不同實現,既不符合程式設計師的程式碼審美,也影響團隊整體的研發效率。

為更好地達成目標 4,團隊決定優先通過重構將既有業務分層,各個版本所必須的業務程式碼抽象下沉為 IM Engine 包,併為各個版本 IM SDK 分別實現不同的 APILayer 以便與既有線上版本介面對齊,這樣既可以降低團隊的研發成本,也可以滿足既有線上客戶後續的升級需求。


(重構程式碼,業務分層)

完成分層後,對於 IM SDK 有依賴的其他產品如 RTC SDK,也都可以擺脫對 IM SDK 介面的依賴而直接呼叫 Engine 層介面,業務層在擴充 RTC 業務時,也就無需再考慮 IM SDK 的版本問題。


(保證擴充性)

做分層的另一個考慮還為了達成目標 2,將與業務層的互動限制在 API 層,在 Engine 中處理 Electron 與 Browser 兩種執行時下的程式碼差異,業務層只需關心 IM SDK 的介面呼叫而無需關心底層差異,確保業務層在兩種執行時下只需要維護極少甚至無需維護相容程式碼,便於業務層更專注於業務開發。

2. Electron 與 Browser 平臺下 IM SDK 的區分

在將 Engine 與業務層隔離後,需要考慮 Engine 在不同的執行時下的關鍵能力差異,並依據能力差異落實 Engine 的底層設計。

在 Browser 與 Electron 平臺下,從連線管理、到訊息收發等實現方式迥異,團隊需要對 Engine 包繼續分層,通過 AEngine 抽象類來定義 IM Engine 的能力介面,並抽象 APIContext 類用來管理 AEngine 的能力呼叫。

考慮到純 Web 應用構建尺寸問題,Electron 的能力實現程式碼不應被打包到標準 Web 頁面內,因此還需要將 Electron 平臺下的實現程式碼單獨抽離出來作為一個獨立包(ElectronSolution),作為可選模組由開發者選擇安裝使用。


(程式碼抽離為可選模組)

如上圖所示,CppEngine 在 ElectronSolution 包中定義,其需要由開發者在 Electron 應用建立 BrowserWindow 例項時通過 webPreferences.preload 配置屬性向渲染程式視窗預掛載。

APIContext 在初始化 AEngine 例項時,優先檢測 CppEngine 是否已定義。當發現有 CppEngine 定義時,則初始化 CppEngine 提供更豐富的本地化能力,否則初始化 JSEngine。

const engine: AEngine = typeof CppEngine !== 'undefined'
  ? new CppEngine()
  : new JSEngine()

3. 解決多程式訊息同步、多程式連線共享問題

ElectronSolution 包截止目前的設計中,所有程式碼都執行在渲染程式內,這意味著每個程式彼此獨立,都在維護獨立的程式狀態,無法滿足目標 3 中多程式狀態同步、連線共享的需求。

為了解決該問題,需要將 CppProto.node 模組放到主程式,在主程式中實現連線管理、訊息收發等能力,多個渲染程式通過 IPC 通訊共享主程式狀態。


(多個渲染程式通過 IPC 通訊共享主程式狀態)

為了達成目標 3 的要求,ElectronSolution 需要拆分為兩個子包:Main 與 Renderer

Main 包執行在主程式內,負責維持 CppProto.node 模組的呼叫,實現底層連線管理、訊息管理等功能,同時通過 Electron 提供的 ipcMain 與各渲染程式維持通訊

Renderer 包中定義 CppEngine 類,繼承自 Engine 包內的 AEngine 抽象類,依然通過 webPreferences.preload 用來作為主程式的代理,通過 ipcRenderer 與主程式維持通訊


(Main 與 Renderer 兩個子包)

修改完成後,ElectronSolution 包的整體結構基本確定,以下列出 ElectronSolution 包關鍵目錄結構供參考。

node_modules/@rongcloud/electron-solution
├── index.js
├── main
│   ├── addon
│   │   ├── binding
│   │   │   └── electron-v{electron-version}-{platform}-{arch}.node
│   │   └── index.js
│   ├── dist
│   │   └── index.js
│   ├── index.js
│   └── package.json
└── renderer
│   ├── dist
│   │   └── index.js
│   ├── index.js
│   └── package.json
└── package.json

基於上述架構變動,當業務層需要在多個渲染程式中實現 IM 能力時,僅需要關注在各個程式中的 IM SDK 介面呼叫,由 ElectronSolution 處理多程式之間的狀態同步問題。

當開發者期望由既有 Web 業務向 Electron 平臺遷移時,開發者也無需修改既有的 Web 業務程式碼,僅需要增量編寫主程式程式碼相關功能實現,將 ElectronSolution 安裝並整合到 Electron 桌面端應用中即可。

整體結構展示

融雲 Electron 平臺的未來規劃

融雲作為安全、可靠的全球網際網路通訊雲服務商,除了在 IM 相關業務的持續深耕,後續還會在該平臺下發力 RTC 場景能力。

目前 Electron 平臺下由 Chromium 原始提供的 WebRTC 能力對於開發桌面級音視訊應用軟體來說相對薄弱,融雲將探索如何藉助 node.js 的擴充能力,提供更為底層的 WebRTC 能力擴充如音效、音質、視訊特效等。

除了更深入的能力擴充,融雲還將提供一個全方面的桌面級應用開發框架,為開發者提供更為全面的 Electron 能力封裝,規範化開發者的程式碼組織、指令碼構建過程,以便於開發者在處理複雜的大型桌面應用時,更專注於業務本身。

同時,融雲一直堅持對技術的持續性投入,不斷夯實底層技術,強化基石力量。對於 Electron 之外的前端技術不斷探索,同時堅持自我迭代,堅持技術卓越,以期為廣大開發者帶來更多實用且易用的產品。

相關文章