二進制序列化在.NET中有很多使用場景,如我們使用分布式緩存時,通常將緩存對象序列化為二進制數據進行緩存,在ASP.NET中,很多中間件(如認證等)也都是用了二進制序列化。
在.NET中我們通常使用System.Runtime.Serialization.Formatters庫中的BinaryFormatter來進行二進制序列化,但此庫存在以下缺點:
- 盡管.net core對BinaryFormatter進行了一些列優化,但其性能還是較低
- 序列化結果尺寸過大,BinaryFormatter保留了非常詳細的類型元數據。
- 安全問題,BinaryFormatter 因為其強大的功能和易用性而廣泛用於整個 .NET 生態系統。 但是,其強大的功能也讓攻擊者能夠影響目標應用內的控制流。 成功的攻擊可能導致攻擊者能夠在目標進程的上下文中運行代碼。(可參考此文檔)
- 通過AssemblyLoadContext動態加載程序集可能無法反序列化的問題(比如使用[PluginFactory]插件框架),例如,你在公共庫A中封裝了序列化輔助方法,在插件程序集B中聲明了序列化類型,並通過公共庫A中的輔助方法進行序列化或反序列化,最后主程序集C通過獨立的AssemblyLoadContext動態加載插件程序集B,此種場景中,B中反序列化時將會引發無法找到程序集的異常。
- 序列化類,必須通過SerializableAttribute特性進行標注
為了解決這些缺陷,我們開源了一款獨立的高性能.NET二進制序列化庫Xfrogcn.BinaryFormatter([Github] [Gitee]),該庫參考了System.Text.Json庫,通過Span與Emit大大提升了序列化性能。此庫目標為.NET Standard 2.1。
Xfrogcn.BinaryFormatter具有以下優點:
- 高性能,通過Span與Emit大大提升了性能,其性能超過System.Runtime.Serialization.Formatters庫的近四倍
- 更小的序列化尺寸(75%)
- 簡單易用,與System.Text.Json基本一致的API接口。
- 反序列化時實例引用的維持
-
類型元數據保留,無需在反序列化時指定目標類型
- 支持反序列化到不同的類型
- 更安全
- 支持AssemblyLoadContext動態加載程序集中類型的序列化
- 無需SerializableAttribute特性標注
- 完善的內置類型支持([支持的類型])
一、性能
與.NET內置的System.Runtime.Serialization.Formatters.Binary.BinaryFormatter二進制序列化對比,性能最高可達到它的4倍以上,而序列化結果的大小僅只有它的75%。
以下為通過test/BinaryFormatter.Benchmark性能測試項目獲取的性能數據,其中:
- Json指System.Text.Json,可以看到其性能的確強悍
- XfrogcnBinary指本庫
- SystemBinaryFormatter指.NET內置二進制序列化庫(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter)
- 類別Stream為采用流化方式序列化
- 類別Bytes為直接序列化為Byte數組或從Byte數組反序列化 所有的測試都基於默認配置,(流化方式下默認的緩沖區大小將會明顯影響序列化性能)
序列化
Method | Categories | Mean |
Json | Stream | 61.41 μs |
XfrogcnBinary | Stream | 92.97 μs |
SystemBinaryFormatter | Stream | 291.37 μs |
Json_Bytes | Bytes | 59.79 μs |
XfrogcnBinary_Bytes | Bytes | 88.67 μs |
反序列化
Method | Categories | Mean |
Json | Stream | 100.12 μs |
XfrogcnBinary | Stream | 96.34 μs |
SystemBinaryFormatter | Stream | 334.68 μs |
Json_Bytes | Bytes | 80.13 μs |
XfrogcnBinary_Bytes | Bytes | 92.14 μs |
二、如何使用
Xfrogcn.BinaryFormatter庫的使用非常簡單,基本與System.Text.Json一致:
序列化
序列化到流:
MemoryStream ms = new MemoryStream(); await Xfrogcn.BinaryFormatter.BinarySerializer.SerializeAsync(ms, data);
序列化到byte數組:
var data = Xfrogcn.BinaryFormatter.BinarySerializer.Serialize(data);
反序列化
從流中反序列化:
var obj = await Xfrogcn.BinaryFormatter.BinarySerializer.DeserializeAsync(stream);
從byte數組反序列化:
var obj = Xfrogcn.BinaryFormatter.BinarySerializer.Deserialize(data);
反序列化為指定類型:
var obj = await Xfrogcn.BinaryFormatter.BinarySerializer.DeserializeAsync<T>(stream); 或者: var obj = Xfrogcn.BinaryFormatter.BinarySerializer.Deserialize<T>(data);
當然,你也可以在序列化與反序列化時指定不同的配置(),更詳細的使用說明請參考[快速開始]
三、注意事項
- 與System.Text.Json的設計一致,由於類型解析、序列化轉換器等緩存都是以配置實例為基礎,即每一個配置實例的緩存是獨立的,故請使用共享的配置實例,請勿為每一次序列化分配新的配置實例
- 在流模式下,默認緩沖區的大小會極大地影響讀取性能,請根據實際情況進行詳細的測試以獲取合適的緩沖區設置(默認設置可適合大多數場景)
開源需要大家的努力,有興趣的同學,歡迎提交代碼,一起完善!