Lua 表的差異同步

ArielleFitz 7年前發布 | 73K 次閱讀 Lua Lua開發

最近同事碰到的一個需求:需要頻繁把一組數據在 skynet 中跨網絡傳遞,而這組數據實際變化并不頻繁,所以做了大量重復的序列化和傳輸工作。

更具體一點說,他在 skynet 中設計了一個網關節點,這個網關服務可以負責把一條消息廣播給一組客戶端,每個客戶端由內部的一個 uuid 串識別,而每條消息都附帶有客戶端 uuid 列表。而實際上這些 uuid 列表組有大量的重復。每條廣播消息都重復打包了列表組,且列表組有大量重復信息。

一開始我想的方法是專門針對這個需求設計一組協議,給發送過的數據組編上 id ,然后在發送方和接收方都根據 id 壓縮通訊數據。即,第一次發送時,發送全量信息,之后再根據數據變化發送差異;如果完全沒有變化,則只需要發送 id 。

之后我想,能不能設計一種較為通用的差異同步方法,可以在跨節點傳遞數據組的時候,避免將相同的數據重復傳輸,而采用差異同步的方法同步對象。

晚飯后,我試著實現了一個這樣一個簡單的模塊。

https://github.com/cloudwu/syncobj

我們可以先在通訊的兩端建立對等的通道,構造數據的一端用 syncobj.source() 創建一個發送通道;而同步數據的另一端用 syncobj.clone() 創建一個接收通道。

當我們在發送端需要構造一組數據的時候,利用 obj = source:new() 創建出一個依附在這個通道上的對象 obj;為了簡化實現,這個對象是一個單層的 lua 表。

我們可以在發送端像一個普通表一樣訪問這個對象,當我們需要同步到另一端時,可以用 diff = source:diff(obj) 生成一個和上個版本之間的差異數據;然后可以把 patch 作為普通的對象序列化發送。

而接收端用 cobj = clone:patch(diff) 就可以還原這個對象。由于 diff 中只是每次對象變化的差異數據,所以數據量會比直接全量傳輸 obj 要小。

如果通訊過程出了問題,我們還可以用 diff = source:reset(obj) 生成一個全量數據,而接收端則可以無視差異包和全量包的差別,都可以用 clone:patch(diff) 還原數據。

在內部協議中,實際上是給每組數據附加了一個單調遞增的 id 號的,這個 id 號包含在 diff 數據中,所以 clone:patch 可以正確識別出如何還原數據。source:reset 生成全量包的過程,其實是賦予了對象一個全新的 id 。

當然,這個同步方案長期工作的話,就會存在一些對象已經在 source 端廢棄,lua 的 gc 雖然會回收那些不再使用的對象內存,但 clone 端并不知曉;另外 source:reset 也會在 clone 端制造垃圾(老的版本已經廢棄)。

所以,還提供了 removeset = source:collect() 返回一個不再使用的 id 列表;只要定期調用,把返回值傳遞給 clone:collect(removeset) 就可以讓接收端清理不用對象。

 

來自:http://blog.codingnow.com/2017/05/syncobj.html

 

 本文由用戶 ArielleFitz 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!