如何為asp.net core添加protobuf支持詳解
前言
在一些性能要求很高的應(yīng)用中,使用protocol buffer序列化,優(yōu)于Json。而且protocol buffer向后兼容的能力比較好。
由于Asp.net core 采用了全新的MiddleWare方式,因此使用protobuf序列化,只需要使用Protobuf-net修飾需要序列化的對(duì)象,并在MVC初始化的時(shí)候增加相應(yīng)的Formatter就可以了。
沒(méi)時(shí)間解釋了,快上車。
通過(guò)NuGet獲取Zaabee.AspNetCoreProtobuf
Install-Package Zaabee.AspNetCoreProtobuf
在Startup.cs文件中修改ConfigureServices方法
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.AddProtobufSupport(); }); }
搞掂……這時(shí)候你就可以通過(guò)application/x-protobuf的content-type來(lái)讓asp.net core使用protobuf來(lái)進(jìn)行序列化/反序列化。
測(cè)試代碼
在asp.net core項(xiàng)目中添加以下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 }
新建一個(gè)XUnit項(xiàng)目,通過(guò)Nuget引用Microsoft.AspNetCore.TestHost,建立一個(gè)測(cè)試類
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?
因?yàn)榭臁谖覀冞@邊使用業(yè)務(wù)數(shù)據(jù)的測(cè)試中,protobuf的序列化/反序列化性能大概是Json.net的三倍,序列化后的體積大概只有Json的二分之一,這可以在相當(dāng)程度上提高webapi的吞吐性能。
另外就是Json對(duì)于浮點(diǎn)數(shù)的處理存在精度丟失,因?yàn)镴S的number類型的安全整數(shù)是53位。當(dāng)我們使用雪花算法來(lái)提供全局遞增ID時(shí)會(huì)因?yàn)榫葋G失導(dǎo)致重復(fù)主鍵。而且情況不僅如此,由于同樣原因傳遞DateTime類型也會(huì)因?yàn)楹撩氩灰恢聦?dǎo)致時(shí)間匹配錯(cuò)誤。一般的解決方法是使用字符串傳遞,不過(guò)這畢竟屬于偏方并沒(méi)有從根源上解決問(wèn)題,因此我們還是直接使用protobuf來(lái)處理。
protobuf的缺點(diǎn)
DTO層必須引用protobuf-net來(lái)添加特性,這在一定程度上導(dǎo)致了代碼的侵入。基本上DTO屬于POCO,依賴第三方包的話總覺(jué)得有點(diǎn)不貞潔……另外就是protobuf序列化后的數(shù)據(jù)不具有可視化,因此如果是使用消息隊(duì)列或者請(qǐng)求監(jiān)控的地方,就要綜合考慮protobuf是否適合使用場(chǎng)景。
原理
asp.net core是基于中間件方式來(lái)實(shí)現(xiàn),其自帶默認(rèn)的JsonFormater(基于Json.net),asp.net core會(huì)根據(jù)content type來(lái)選擇對(duì)應(yīng)的Formater來(lái)處理對(duì)象的序列化,當(dāng)中包括InputFormatter(反序列化)和OutputFormatter(序列化)。因此除了protobuf,我們還可以添加或者替換其它的序列化方式,例如使用Jil來(lái)代替Json.net來(lái)提高Json性能。
以上實(shí)現(xiàn)以及Demo和測(cè)試的源代碼已放到 GitHub 上。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
.Net Core 之 Ubuntu 14.04 部署過(guò)程(圖文詳解)
本篇文章主要介紹了.Net Core 之 Ubuntu 14.04 部署過(guò)程(圖文詳解),有興趣的可以了解一下。2016-11-11asp.net使用DataTable構(gòu)造Json字符串的方法
這篇文章主要介紹了asp.net使用DataTable構(gòu)造Json字符串的方法,涉及asp.net字符串序列化、遍歷及構(gòu)造等操作技巧,需要的朋友可以參考下2015-12-12Gridview的鏈接和刪除點(diǎn)擊提示的問(wèn)題探討
Gridview的鏈接和刪除點(diǎn)擊提示有利于提醒用戶確認(rèn)下操作是否正確,以免造成不必要的損失,感興趣的朋友可以參考下,希望對(duì)你有所幫助2013-04-04asp.net 獨(dú)立Discuz頭像編輯模塊分離打包
在Discuz產(chǎn)品系列(包括UCenter、UCHome)中有一個(gè)flash頭像上傳編輯的功能比較好用,和之前自己用js實(shí)現(xiàn)的照片在線編輯插件比較像,于是想將它獨(dú)立出來(lái),一方面可以學(xué)習(xí)研究,另一方面有機(jī)會(huì)可以在項(xiàng)目中使用(這里主要是指Asp.Net程序,php的與之類似)。2011-06-06不可或缺的ASP.NET內(nèi)置對(duì)象小結(jié)
這篇文章主要介紹了不可或缺的ASP.NET內(nèi)置對(duì)象小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04基于.NET BitmapImage 內(nèi)存釋放問(wèn)題的解決方法詳解
本篇文章是對(duì).NET BitmapImage 內(nèi)存釋放問(wèn)題的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Web.config(應(yīng)用程序的配置信息)總結(jié)
Web.config文件是一個(gè)XML文本文件,它用來(lái)儲(chǔ)存 ASP.NET Web 應(yīng)用程序的配置信息(如最常用的設(shè)置ASP.NET Web 應(yīng)用程序的身份驗(yàn)證方式),它可以出現(xiàn)在應(yīng)用程序的每一個(gè)目錄中,接下來(lái)詳細(xì)介紹一下配置情況,感興趣的朋友可以了解下2013-01-01在ASP.NET中支持?jǐn)帱c(diǎn)續(xù)傳下載大文件(ZT)源碼
這篇文章主要為大家介紹了在ASP.NET中如何做到支持?jǐn)帱c(diǎn)續(xù)傳下載大文件(ZT),需要的朋友可以參考下2014-07-07