当前位置: 移动技术网 > IT编程>开发语言>.net > 如何为asp.net core添加protobuf支持详解

如何为asp.net core添加protobuf支持详解

2018年03月06日  | 移动技术网IT编程  | 我要评论

18dydy,上海到舟山,领导班子 小说

前言

在一些性能要求很高的应用中,使用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 上。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网