最近在研究web api 2,看了一篇文章,讲解如何提升性能的,
在序列化速度的跑分中,Protobuf一骑绝尘,序列化速度快,性能强,体积小,所以打算了解下这个利器
1:安装篇
谷歌官方没有提供.net的实现,所以在nuget上找了一个移植的
Nuget里搜索Protobuf-net,下载,自动添加到项目中
2:定义数据结构
using ProtoBuf; namespace ConsoleApplication1 { [ProtoContract] class Person { [ProtoMember(1)] public int Id { get; set; } [ProtoMember(2)] public string Name { get; set; } [ProtoMember(3)] public Address Address { get; set; } } [ProtoContract] class Address { [ProtoMember(1)] public string Line1 { get; set; } [ProtoMember(2)] public string Line2 { get; set; } } }
3:封装简单操作类
按照作者使用习惯,简单提供了一个Helper类
using System.IO; using System.Text; using ProtoBuf; namespace ConsoleApplication1 { public class ProtobufHelper { /// <summary> /// 序列化 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> public static string Serialize<T>(T t) { using (MemoryStream ms = new MemoryStream()) { Serializer.Serialize<T>(ms, t); return Encoding.UTF8.GetString(ms.ToArray()); } } /// <summary> /// 反序列化 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="content"></param> /// <returns></returns> public static T DeSerialize<T>(string content) { using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(content))) { T t = Serializer.Deserialize<T>(ms); return t; } } } }
4:操作体验
代码很简单,就不分开贴了
using System; using System.Collections.Generic; using System.IO; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var p1 = new Person { Id = 1, Name = "八百里开外", Address = new Address { Line1 = "Line1", Line2 = "Line2" } }; var p2 = new Person { Id = 2, Name = "一枪", Address = new Address { Line1 = "Flat Line1", Line2 = "Flat Line2" } }; List<Person> pSource = new List<Person>() { p1, p2 }; string content = ProtobufHelper.Serialize<List<Person>>(pSource); Console.Write(content); //写入文件 File.WriteAllText("D://hello.txt", content); Console.WriteLine("\r\n****解析部分*****"); List<Person> pResult = ProtobufHelper.DeSerialize<List<Person>>(content); foreach (Person p in pResult) { Console.WriteLine(p.Name); } Console.Read(); } } }
控制台运行结果
同样的数据,和Json所占用空间对比,高下立判
后记
protobuf虽然有千般好,但是我们是在 web api上使用的,前台js解析不了Protobuf,所以只能用Json咯~!
StackService虽然Github上有2K多个Star,但是收费的。。同样的事情web api 2也能做到,所以也略过它。
最终作者选择了跑分测试里面的第二名Jil https://github.com/kevin-montrose/Jil
1. With very minimal annotation on the class level
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] // only required on the class level class PersonEntity { public string FirstName { get; set; } public string LastName { get; set; } }
2. Without any annotation (using RuntimeTypeModel)
static void InitializeProtobufRunTime() { var assembly = Assembly.GetAssembly(typeof(PlainEntities.PersonEntity)); var types = assembly.GetTypes(); foreach (var t in types.Where(x => x.Namespace.Contains("PlainEntities"))) { Console.WriteLine("Processing {0}", t.FullName); var meta = RuntimeTypeModel.Default.Add(t, false); var index = 1; // find any derived class for the entity foreach (var d in types.Where(x => x.IsSubclassOf(t))) { var i = index++; Console.WriteLine("\tSubtype: {0} - #{1}", d.Name, i); meta.AddSubType(i, d); } // then add the properties foreach (var p in t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).Where(x => x.GetSetMethod() != null)) { var i = index++; Console.WriteLine("\tProperty: {0} - #{1}", p.Name, i); meta.AddField(i, p.Name); } } }
And both the above works quite well without any performance differences.
------------------
TestBinaryEntities
------------------
Process: 100000 items, MemorySize: 7400705, Completed in: 3877 ms, Serialization took: 676 ms, Deserialization took: 2948 ms
----------------------------------
TestProtobufFullyAnnotatedEntities
----------------------------------
Process: 100000 items, MemorySize: 3983490, Completed in: 682 ms, Serialization took: 164 ms, Deserialization took: 253 ms
-------------------------------------
TestProtobufImplicitAnnotatedEntities
-------------------------------------
Process: 100000 items, MemorySize: 3983490, Completed in: 595 ms, Serialization took: 104 ms, Deserialization took: 210 ms
-------------------------------
TestProtobufRuntimeRegistration
-------------------------------
Processing ProtobufTestConsole.PlainEntities.BaseEntity
Subtype: PersonEntity - #1
Property: Id - #2
Property: Gender - #3
Processing ProtobufTestConsole.PlainEntities.PersonEntity
Property: FirstName - #1
Property: LastName - #2
Property: Age - #3
Process: 100000 items, MemorySize: 4083490, Completed in: 646 ms, Serialization took: 113 ms, Deserialization took: 232 ms
Looking forward to get this in :)
Also attached the sample project for reference