1. .Net Core的序列化方式 1.1 json.Net 常用的工具包,如Newtonsoft.Json, 它是基於json格式的序列化和反序列化的組件 json.net 有以下優點: 侵入性:可以不添加attribute,就能進行序列化操作 靈活性:可以靈活性配置,比如允許被序列化的成員自定 ...
1. .Net Core的序列化方式
1.1 json.Net
常用的工具包,如Newtonsoft.Json, 它是基於json格式的序列化和反序列化的組件
json.net 有以下優點:
侵入性:可以不添加attribute,就能進行序列化操作
靈活性:可以靈活性配置,比如允許被序列化的成員自定義名字,屏蔽的非序列化屬性成員
可讀性: 數據格式比較簡單, 易於讀寫
依賴性:可以序列化成JObject,無需依賴對象進行序列化和泛型化。
1.2 protobuf
它是基於二進位格式的序列化和反序列化的組件
protobuf 有以下優點:
性能高 : 序列化後體積相比Json和XML很小,適合RPC二進位傳輸
跨語言:支持跨平臺多語言
相容性:消息格式升級和相容性還不錯
速度快 :序列化反序列化速度很快,快於Json的處理速度
1.3 messagepack
它是基於二進位格式的序列化和反序列化的組件
messagepack有以下優點:
性能高:序列化後體積相比Json和XML很小,適合RPC二進位傳輸
跨語言:支持跨平臺多語言
相容性:消息格式升級和相容性還不錯
速度快 :序列化反序列化速度很快,快於Json的處理速度
messagepack不管是小數據量還是大數據量都保持比較穩定的性能,本文中使用messagepack序列化方式。
2. 項目編碼及設計模式
如下是文件結構:
2.1 工廠模式
抽象介面 工廠負責創建編碼器和解碼器
1.工廠
/// <summary> /// 一個抽象的傳輸消息編解碼器工廠。 /// </summary> public interface ITransportMessageCodecFactory { /// <summary> /// 獲取編碼器。 /// </summary> /// <returns>編碼器實例。</returns> ITransportMessageEncoder GetEncoder(); /// <summary> /// 獲取解碼器。 /// </summary> /// <returns>解碼器實例。</returns> ITransportMessageDecoder GetDecoder(); }
2.編碼器
/// <summary> /// 編碼器 /// </summary> public interface ITransportMessageEncoder { byte[] Encode(TransportMessage message); }
3.解碼器
/// <summary> /// 解碼器 /// </summary> public interface ITransportMessageDecoder { TransportMessage Decode(byte[] data); }
實現類 工廠、編碼器、解碼器為MessagePack的實現
1.工廠
public sealed class MessagePackTransportMessageCodecFactory : ITransportMessageCodecFactory { #region Field private readonly ITransportMessageEncoder _transportMessageEncoder = new MessagePackTransportMessageEncoder(); private readonly ITransportMessageDecoder _transportMessageDecoder = new MessagePackTransportMessageDecoder(); #endregion Field #region Implementation of ITransportMessageCodecFactory /// <inheritdoc /> /// <summary> /// 獲取編碼器 /// </summary> /// <returns></returns> public ITransportMessageEncoder GetEncoder() { return _transportMessageEncoder; } /// <inheritdoc /> /// <summary> /// 獲取解碼器 /// </summary> /// <returns></returns> public ITransportMessageDecoder GetDecoder() { return _transportMessageDecoder; } #endregion Implementation of ITransportMessageCodecFactory }
2.編碼器
public sealed class MessagePackTransportMessageEncoder : ITransportMessageEncoder { #region Implementation of ITransportMessageEncoder public byte[] Encode(TransportMessage transportMessage) { MessagePackTransportMessage messagePackTransportMessage = new MessagePackTransportMessage(transportMessage); return MessagePackSerializer.Serialize(messagePackTransportMessage); } #endregion Implementation of ITransportMessageEncoder }
3.解碼器
public sealed class MessagePackTransportMessageDecoder : ITransportMessageDecoder { #region Implementation of ITransportMessageDecoder public TransportMessage Decode(byte[] data) { MessagePackTransportMessage messagePackTransportMessage = MessagePackSerializer.Deserialize<MessagePackTransportMessage>(data); return messagePackTransportMessage.GetTransportMessage(); } #endregion Implementation of ITransportMessageDecoder }
2.2 裝飾器模式
高層的消息模型:
public class TransportMessage { /// <summary> /// 消息Id。 /// </summary> public string Id { get; set; } /// <summary> /// 消息內容。 /// </summary> public object Content { get; set; } /// <summary> /// 內容類型。 /// </summary> public string ContentType { get; set; } }
由於MessagePack序列化方式具有侵入性,需要添加 MessagePackObjectAttribute 和 KeyAttribute 特性,因此需要對 TransportMessage 做裝飾:
using MessagePack; [MessagePackObject] public class MessagePackTransportMessage { private TransportMessage _transportMessage; public MessagePackTransportMessage(): this(new TransportMessage()) { } public MessagePackTransportMessage(TransportMessage transportMessage) { this._transportMessage = transportMessage; } public TransportMessage GetTransportMessage() { return _transportMessage; } /// <summary> /// 消息Id。 /// </summary> [Key(0)] public string Id { get { return _transportMessage.Id; } set { _transportMessage.Id = value; } } /// <summary> /// 消息內容。 /// </summary> [Key(1)] public object Content { get { return _transportMessage.Content; } set { _transportMessage.Content = value; } } /// <summary> /// 內容類型。 /// </summary> [Key(2)] public string ContentType { get { return _transportMessage.ContentType; } set { _transportMessage.ContentType = value; } } }
2.3 依賴註入
Autofac 是一個依賴註入工具包,比.net Core 原始的依賴註入擁有更完善的功能,中文官方文檔:https://autofaccn.readthedocs.io/zh/latest/index.html
using Autofac; public static class ContainerBuilderExtensions { /// <summary> /// 使用messagepack編碼解碼方式 /// </summary> /// <param name="builder"></param> /// <returns></returns> public static ContainerBuilder UseMessagePackCodec(this ContainerBuilder builder) { builder.RegisterType(typeof(MessagePackTransportMessageCodecFactory)).As(typeof(ITransportMessageCodecFactory)).SingleInstance(); return builder; } }
2.4 單元測試
端到端的測試,同時測試編碼和解碼
using MessagePack; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MessagePackTest { [TestMethod] public void TestCodec() { Person person = new Person { Name = "張巨集偉", Age = 18 }; TransportMessage transportMessage = new TransportMessage { Id = "1", ContentType = "Person", Content = person }; MessagePackTransportMessageCodecFactory factory = new MessagePackTransportMessageCodecFactory(); ITransportMessageEncoder encoder = factory.GetEncoder(); ITransportMessageDecoder decoder = factory.GetDecoder(); byte[] vs = encoder.Encode(transportMessage); TransportMessage message =decoder.Decode(vs); Assert.AreEqual(message.Id, "1"); Assert.AreEqual(message.ContentType, "Person"); Assert.AreEqual(((object[])message.Content)[0].ToString(), "張巨集偉" ); Assert.AreEqual(((object[])message.Content)[1].ToString(), "18"); } [MessagePackObject] public class Person { [Key(0)] public string Name { get; set; } [Key(1)] public int Age { get; set; } } }