如何为asp.net core添加protobuf支持详解
前言
在一些性能要求很高的应用中,使用protocol buffer序列化,优于json。而且protocol buffer向后兼容的能力比较好。
由于asp.net core 采用了全新的middleware方式,因此使用protobuf序列化,只需要使用protobuf-net修饰需要序列化的对象,并在mvc初始化的时候增加相应的formatter就可以了。
没时间解释了,快上车。
通过nuget获取zaabee.aspnetcoreprotobuf
install-package zaabee.aspnetcoreprotobuf
在startup.cs文件中修改configureservices方法
public void configureservices(iservicecollection services) { services.addmvc(options => { options.addprotobufsupport(); }); }
搞掂……这时候你就可以通过application/x-protobuf的content-type来让asp.net core使用protobuf来进行序列化/反序列化。
测试代码
在asp.net core项目中添加以下dto
[protocontract] public class testdto { [protomember(1)] public guid id { get; set; } [protomember(2)] public string name { get; set; } [protomember(3)] public datetime createtime { get; set; } [protomember(4)] public list<testdto> kids { get; set; } [protomember(5)] public long tag { get; set; } [protomember(6)] public testenum enum { get; set; } } public enum testenum { apple, banana, pear }
新建一个xunit项目,通过nuget引用microsoft.aspnetcore.testhost,建立一个测试类
public class aspnetcoreprotobuftest { private readonly testserver _server; private readonly httpclient _client; public aspnetcoreprotobuftest() { _server = new testserver( new webhostbuilder() .usekestrel() .usestartup<startup>()); _client = _server.createclient(); } [fact] public void test() { // http post with protobuf response body _client.defaultrequestheaders.accept.add(new mediatypewithqualityheadervalue("application/x-protobuf")); var dtos = getdtos(); var stream = new memorystream(); protobuf.serializer.serialize(stream, dtos); httpcontent httpcontent = new streamcontent(stream); // http post with protobuf request body var responseforpost = _client.postasync("api/values", httpcontent); var result = protobuf.serializer.deserialize<list<testdto>>( responseforpost.result.content.readasstreamasync().result); assert.true(comparedtos(dtos,result)); } private static bool comparedtos(list<testdto> lstone, list<testdto> lsttwo) { lstone = lstone ?? new list<testdto>(); lsttwo = lsttwo ?? new list<testdto>(); if (lstone.count != lsttwo.count) return false; for (var i = 0; i < lstone.count; i++) { var dtoone = lstone[i]; var dtotwo = lsttwo[i]; if (dtoone.id != dtotwo.id || dtoone.createtime != dtotwo.createtime || dtoone.enum != dtotwo.enum || dtoone.name != dtotwo.name || dtoone.tag != dtotwo.tag || !comparedtos(dtoone.kids, dtotwo.kids)) return false; } return true; } private static list<testdto> getdtos() { return new list<testdto> { new testdto { id = guid.newguid(), tag = long.maxvalue, createtime = datetime.now, name = "0", enum = testenum.apple, kids = new list<testdto> { new testdto { id = guid.newguid(), tag = long.maxvalue - 1, createtime = datetime.now, name = "00", enum = testenum.banana }, new testdto { id = guid.newguid(), tag = long.maxvalue - 2, createtime = datetime.now, name = "01", enum = testenum.pear } } }, new testdto { id = guid.newguid(), tag = long.maxvalue - 3, createtime = datetime.now, name = "1", enum = testenum.apple, kids = new list<testdto> { new testdto { id = guid.newguid(), tag = long.maxvalue - 4, createtime = datetime.now, name = "10", enum = testenum.banana }, new testdto { id = guid.newguid(), tag = long.maxvalue - 5, createtime = datetime.now, name = "11", enum = testenum.pear } } } }; } }
为什么要用protobuf?
因为快……在我们这边使用业务数据的测试中,protobuf的序列化/反序列化性能大概是json.net的三倍,序列化后的体积大概只有json的二分之一,这可以在相当程度上提高webapi的吞吐性能。
另外就是json对于浮点数的处理存在精度丢失,因为js的number类型的安全整数是53位。当我们使用雪花算法来提供全局递增id时会因为精度丢失导致重复主键。而且情况不仅如此,由于同样原因传递datetime类型也会因为毫秒不一致导致时间匹配错误。一般的解决方法是使用字符串传递,不过这毕竟属于偏方并没有从根源上解决问题,因此我们还是直接使用protobuf来处理。
protobuf的缺点
dto层必须引用protobuf-net来添加特性,这在一定程度上导致了代码的侵入。基本上dto属于poco,依赖第三方包的话总觉得有点不贞洁……另外就是protobuf序列化后的数据不具有可视化,因此如果是使用消息队列或者请求监控的地方,就要综合考虑protobuf是否适合使用场景。
原理
asp.net core是基于中间件方式来实现,其自带默认的jsonformater(基于json.net),asp.net core会根据content type来选择对应的formater来处理对象的序列化,当中包括inputformatter(反序列化)和outputformatter(序列化)。因此除了protobuf,我们还可以添加或者替换其它的序列化方式,例如使用jil来代替json.net来提高json性能。
以上实现以及demo和测试的源代码已放到 github 上。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。