ET介紹——強大的MongoBson庫

来源:https://www.cnblogs.com/flamesky/archive/2023/05/18/17413687.html
-Advertisement-
Play Games

強大的MongoBson庫 後端開發,統計了一下大概有這些場景需要用到序列化: 對象通過序列化反序列化clone 服務端資料庫存儲數據,二進位 分散式服務端,多進程間的消息,二進位 後端日誌,文本格式 服務端的各種配置文件,文本格式 C#序列化庫有非常非常多了,protobuf,json等等。但是這 ...


強大的MongoBson庫

後端開發,統計了一下大概有這些場景需要用到序列化:

  1. 對象通過序列化反序列化clone
  2. 服務端資料庫存儲數據,二進位
  3. 分散式服務端,多進程間的消息,二進位
  4. 後端日誌,文本格式
  5. 服務端的各種配置文件,文本格式

C#序列化庫有非常非常多了,protobuf,json等等。但是這些序列化庫都無法應當所有場景,既要可讀又要小。protobuf不支持複雜的對象結構(無法使用繼承),做消息合適,做資料庫存儲和日誌格式並不好用。json做日誌格式合適,但是做網路消息和數據存儲就太大。我們當然希望一個庫能滿足上面所有場景,理由如下:

  1. 你想想某天你的配置文件需要放到資料庫中保存,你不需要進行格式轉換,後端直接把前端發過來的配置消息保存到資料庫中,這是不是能減少非常多錯誤呢?
  2. 某天有些服務端的配置文件不用文件格式了,需要放在資料庫中,同樣,只需要幾行代碼就可以完成遷移。
  3. 某天後端伺服器crash,你需要掃描日誌進行數據恢復,把日誌進行反序列化成C#對象,一條條進行處理,再轉成對象保存到資料庫就完成了。
  4. 對象保存在資料庫,直接就可以看到文本內容,可以做各種類sql的操作
  5. 想像一個場景,一個配置文本對象,反序列化到記憶體,通過網路消息發送,存儲到資料庫中。整個過程一氣呵成。

簡單來說就是減少各種數據轉換,減少代碼,提高開發效率,提高可維護性。當然,Mongo Bson就能夠滿足。MongoDB庫既可以序列化成文本也可以序列化成BSON的二進位格式,並且MongoDB本身就是一個游戲中使用非常多的資料庫。Mongo Bson非常完善,是我見過功能最全使用最強大的序列化庫,有些功能十分貼心。其支持功能如下:

  1. 支持複雜的繼承結構
  2. 支持忽略某些欄位序列化
  3. 支持欄位預設值
  4. 結構多出多餘的欄位照樣可以反序列化,這對多版本協議非常有用
  5. 支持ISupportInitialize介面使用,這個在反序列化的時候簡直就是神器
  6. 支持文本json和二進位bson序列化
  7. MongoDB資料庫支持

簡單的介紹下mongo bson庫

1.支持序列化反序列化成json或者bson

public sealed class Player
    {
        public long Id;

        public string Account { get; private set; }

        public long UnitId { get; set; }
    }

    Player player1 = new Player() { Id = 1 };
    string json = player1.ToJson();
    Console.WriteLine($"player1 to json: {json}");
    Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
    // output:
    // player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
    // player to bson: B000000125F69640001000000000000000A4163636F756E740012556E6974496400000000000000000000

 

註意mongo的json跟標準的json有點區別,如果想用標準的json,可以傳入一個JsonWriterSettings對象,限制使用JsonOutputMode.Strict模式

// 使用標準json
    Player player2 = new Player() { Id = 1 };
    Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
    // player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }

 

反序列化json:

            // 反序列化json
        Player player11 = BsonSerializer.Deserialize<Player>(json);
        Console.WriteLine($"player11 to json: {player11.ToJson()}");

 

反序列化bson:

    // 反序列化bson
    using (MemoryStream memoryStream = new MemoryStream(bson))
    {
        Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
        Console.WriteLine($"player12 to json: {player12.ToJson()}");
    }

 

2.可以忽略某些欄位

[BsonIgnore]該標簽用來禁止欄位序列化。

    public sealed class Player
    {
        public long Id;

        [BsonIgnore]
        public string Account { get; private set; }
        
        public long UnitId { get; set; }
    }

    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
    Console.WriteLine($"player to json: {player.ToJson()}");
    // player to json: { "_id" : 2, "UnitId" : 3 }

 

3.支持預設值以及取別名

[BsonElement] 欄位加上該標簽,即使是private欄位也會序列化(預設只序列化public欄位),該標簽還可以帶一個string參數,給欄位序列化指定別名。

    public sealed class Player
    {
        public long Id;

        public string Account { get; private set; }

        [BsonElement("UId")]
        public long UnitId { get; set; }
    }
    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
    Console.WriteLine($"player to json: {player.ToJson()}");
    // player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }

 

4.升級版本支持

[BsonIgnoreExtraElements] 該標簽用在class上面,反序列化時用來忽略多餘的欄位,一般版本相容需要考慮,低版本的協議需要能夠反 序列化高版本的內容,否則新版本加了欄位,舊版本結構反序列化會出錯

    [BsonIgnoreExtraElements]
    public sealed class Player
    {
        public long Id;

        public string Account { get; private set; }

        [BsonElement("UId")]
        public long UnitId { get; set; }
    }

 

5.支持複雜的繼承結構

mongo bson庫強大的地方在於完全支持序列化反序列化繼承結構。需要註意的是,繼承反序列化需要註冊所有的父類,有兩種方法: a. 你可以在父類上面使用[BsonKnownTypes]標簽聲明繼承的子類,這樣mongo會自動註冊,例如:

    [BsonKnownTypes(typeof(Entity))]
    public class Component
    {
    }
    [BsonKnownTypes(typeof(Player))]
    public class Entity: Component
    {
    }
    public sealed class Player: Entity
    {
        public long Id;
        
        public string Account { get; set; }
        
        public long UnitId { get; set; }
    }

 

這樣有缺陷,因為框架並不知道一個類會有哪些子類,這樣做對框架代碼有侵入性,我們希望能解除這個耦合 。可以掃描程式集中所有子類父類的類型,將他們註冊到mongo驅動中

            Type[] types = typeof(Game).Assembly.GetTypes();
            foreach (Type type in types)
            {
                if (!type.IsSubclassOf(typeof(Component)))
                {
                    continue;
                }

                BsonClassMap.LookupClassMap(type);
            }

            BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));

 

這樣完全的自動化註冊,使用者也不需要關心類是否註冊。

6.ISupportInitialize介面

mongo bson反序列化時支持一個ISupportInitialize介面,ISupportInitialize有兩個方法

    public interface ISupportInitialize
    {
        void BeginInit();
        void EndInit();
    }

 

BeginInit在反序列化前調用,EndInit在反序列化後調用。這個介面非常有用了,可以在反序列化後執行一些操作。例如

    [BsonIgnoreExtraElements]
    public class InnerConfig: AConfigComponent
    {
        [BsonIgnore]
        public IPEndPoint IPEndPoint { get; private set; }
        
        public string Address { get; set; }

        public override void EndInit()
        {
            this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
        }
    }

 

InnerConfig是ET中進程內網地址的配置,由於IPEndPoint不太好配置,我們可以配置成string形式,然後反序列化的時候在EndInit中把string轉換成IPEndPoint。 同樣我給protobuf反序列化方法也加上了這個調用,參考ProtobufHelper.cs,ET的protobuf因為要支持ilruntime,所以去掉了map的支持,假如我們想要一個map怎麼辦呢?這裡我給生成的代碼都做了手腳,把proto消息都改成了partial class,這樣我們可以自己擴展這個class,比如:

message UnitInfo
{
    int64 UnitId  = 1;

    float X = 2;
    float Y = 3;
    float Z = 4;
}

// protobuf
message G2C_EnterMap // IResponse
{
    int32 RpcId = 90;
    int32 Error = 91;
    string Message = 92;
    // 自己的unit id
    int64 UnitId = 1;
    // 所有的unit
    repeated UnitInfo Units = 2;
}

 

這個網路消息有個repeated UnitInfo欄位,在protobuf中其實是個數組,使用起來不是很方便,我希望轉成一個Dictionary<Int64, UnitInfo>的欄位,我們可以做這樣的操作:

    public partial class G2C_EnterMap: ISupportInitialize
    {
        public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>();
        
        public void BeginInit()
        {
        }

        public void EndInit()
        {
            foreach (var unit in this.Units)
            {
                this.unitsDict.Add(unit.UnitId, unit);
            }
        }
    }

 

通過這樣一段代碼把消息進行擴展一下,反序列化出來之後,自動轉成了一個Dictionary。

ET開源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com)   qq群:474643097


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 這兩天,我們經常逛的好多網站、app首頁都變灰了,原因大家應該都知道了 網站變灰 ①B站 ②愛奇藝 ③ 騰訊視頻 ④ csdn ⑤百度 怎麼實現的呢? 難道這些網站開發商在網站開發的時候都準備一套灰色主題的UI麽? 好奇心的驅使下,開始瘋狂的詢問度娘,果真還是找到了這麼一個網站。 ①官方文檔地 ...
  • CSS 實現磨砂玻璃(毛玻璃)效果樣式 要實現磨砂玻璃背景,可以使用 CSS3 中的 ::before 偽元素和 backdrop-filter 屬性,結合 opacity 屬性和 blur() 函數來實現。 具體實現步驟如下: 創建一個具有背景的元素,例如一個 div 元素。 div { back ...
  • 業務場景: 最近需要開發一個基於vue2的項目,再使用Electron 打包成exe文件。 實際操作時發現vue項目組件依賴最高到node16,電腦上的環境是最新的node20 忙著把node20卸載,換上node16,VUE項目是跑進來了,但是使用Electron 時候又發現需要node19才現, ...
  • 一個合格的技術人的內心要時刻謹記自己在一個企業內的價值所在,並不斷的通過技術提升來擴大價值,才可以在當下的環境中,個人價值與企業價值形成正向迴圈。那我們此次就聊一聊,前端職能如何在不同的業務場景中落地價值。 ...
  • AJAX 是Asynchronous JavaScript And XML的縮寫。 它不是一種編程語言。它是一種基於HTML、CSS、JavaScript 和 XML,讓開發更好、更快和更有互動的 Web 應用的技術。 什麼是ajax 認識前後端交互 前後端交互就是前端與後端的一種通訊方式,主要使用 ...
  • 業務場景 二輪充電業務中,用戶充電完成後在訂單詳情頁展示訂單相關信息,用戶點擊分享按鈕喚起微信小程式分享菜單,將生成的圖片海報分享給微信好友或者下載到本地,好友可通過掃描海報中的二維碼加群領取優惠。 使用場景及功能:微信小程式 生成海報圖片 分享好友 下載圖片 使用技術:Taro vue vant ...
  • 完整的可以與資料庫連接的登錄界面的代碼 login.jsp <%@ page language="java" contentType="text/html; UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta chars ...
  • HTTP(Hypertext Transfer Protocol)是一種用於在Web瀏覽器和Web伺服器之間傳輸數據的協議。HTTP的版本有很多,其中比較常見的有 HTTP 1.0 、 HTTP 1.1 和 HTTP 2.0 ,它們有各自的特點。 HTTP 1.0 的特點: 1. 每個請求/響應需要 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...