溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎樣為asp.net core添加protobuf支持

發(fā)布時間:2021-02-18 14:27:33 來源:億速云 閱讀:211 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)怎樣為asp.net core添加protobuf支持的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

前言

在一些性能要求很高的應(yīng)用中,使用protocol buffer序列化,優(yōu)于Json。而且protocol buffer向后兼容的能力比較好。

由于Asp.net core 采用了全新的MiddleWare方式,因此使用protobuf序列化,只需要使用Protobuf-net修飾需要序列化的對象,并在MVC初始化的時候增加相應(yīng)的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來進(jìn)行序列化/反序列化。

測試代碼

在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?

因為快……在我們這邊使用業(yè)務(wù)數(shù)據(jù)的測試中,protobuf的序列化/反序列化性能大概是Json.net的三倍,序列化后的體積大概只有Json的二分之一,這可以在相當(dāng)程度上提高webapi的吞吐性能。

另外就是Json對于浮點數(shù)的處理存在精度丟失,因為JS的number類型的安全整數(shù)是53位。當(dāng)我們使用雪花算法來提供全局遞增ID時會因為精度丟失導(dǎo)致重復(fù)主鍵。而且情況不僅如此,由于同樣原因傳遞DateTime類型也會因為毫秒不一致導(dǎo)致時間匹配錯誤。一般的解決方法是使用字符串傳遞,不過這畢竟屬于偏方并沒有從根源上解決問題,因此我們還是直接使用protobuf來處理。

protobuf的缺點

DTO層必須引用protobuf-net來添加特性,這在一定程度上導(dǎo)致了代碼的侵入?;旧螪TO屬于POCO,依賴第三方包的話總覺得有點不貞潔……另外就是protobuf序列化后的數(shù)據(jù)不具有可視化,因此如果是使用消息隊列或者請求監(jiān)控的地方,就要綜合考慮protobuf是否適合使用場景。

原理

asp.net core是基于中間件方式來實現(xiàn),其自帶默認(rèn)的JsonFormater(基于Json.net),asp.net core會根據(jù)content type來選擇對應(yīng)的Formater來處理對象的序列化,當(dāng)中包括InputFormatter(反序列化)和OutputFormatter(序列化)。因此除了protobuf,我們還可以添加或者替換其它的序列化方式,例如使用Jil來代替Json.net來提高Json性能。

感謝各位的閱讀!關(guān)于“怎樣為asp.net core添加protobuf支持”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI