你的位置:首页 > ASP.net教程

[ASP.net教程]在Wcf中应用ProtoBuf替代默认的序列化器


  Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wcf有一天能在Linux平台上闪瞎所有人。现在简单表述下Wcf中应用ProtoBuf替代默认的序列化器。

准备:

  首先,新建一套Wcf的解决方案,包含服务,宿主外加两个客户端用来测试调用:

  Wcf_ProtoBufSample.ClientViaMetaData会通过添加服务引用的方式调用服务,Wcf_ProtoBufSample.ClientViaReference则直接通过对Wcf_ProtoBufSample.Service添加引用来调动服务。

  分别为每个项目对protobuf-net添加引用: install-package protobuf-net -Version 2.0.0.668(此处现在比较纠结,protobuf-net的最新版本是2.1.0.0,但现在移除了ProtoBuf.ServiceModel,目测是为了兼容dotnet core,估计以后还会再回来的。)

  接下来在Wcf_ProtoBufSample.Service中简单定义个服务:

 [ServiceContract, ProtoContract]  public interface IGreeterService  {    [OperationContract]    Reply Get(Request request);  }  public class GreeterService : IGreeterService  {    public Reply Get(Request request)    {      Reply reply = new Reply() { GreetInfo = "你好!" + request.Name + ",恭喜你" + request.Age + "岁了!" };      return reply;    }  }  [DataContract]  [ProtoContract]  public class Request  {    [DataMember(Order = 0)]    [ProtoMember(1)]    public string Name { set; get; }    [DataMember(Order = 1)]    [ProtoMember(2)]    public int Age { set; get; }  }  [DataContract]  [ProtoContract]  public class Reply  {    [DataMember(Order = 0)]    [ProtoMember(1)]    public string GreetInfo { set; get; }  }

View Code

  代码中对DataMember添加了Order的特性,方便过会用。

配置宿主

  在宿主中进行配置:

 <system.serviceModel>  <services>   <service behaviorConfiguration="GreeterServiceBehavior" name="Wcf_ProtoBufSample.Service.GreeterService">    <endpoint     address="net.tcp://127.0.0.1:6978/GreeterService"     binding="netTcpBinding"     behaviorConfiguration="protoEndpointBehavior"     bindingConfiguration="DefaultTcpBinding"     contract="Wcf_ProtoBufSample.Service.IGreeterService">    </endpoint>    <endpoint     address="net.tcp://127.0.0.1:6976/mex"     binding="mexTcpBinding"     contract="IMetadataExchange">    </endpoint>   </service>  </services>  <behaviors>   <serviceBehaviors>    <behavior name="GreeterServiceBehavior">     <serviceMetadata/>     <serviceDebug includeExceptionDetailInFaults="true"/>    </behavior>   </serviceBehaviors>   <endpointBehaviors>    <behavior name="protoEndpointBehavior">     <protobuf/>    </behavior>   </endpointBehaviors>  </behaviors>  <extensions>   <behaviorExtensions>    <add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67"/>   </behaviorExtensions>  </extensions>  <bindings>   <netTcpBinding>    <binding name="DefaultTcpBinding"      closeTimeout="00:00:30"      openTimeout="00:00:30"      receiveTimeout="00:05:00"      sendTimeout="00:50:00"      transactionFlow="true"      transferMode="Buffered"      listenBacklog="100"      maxBufferPoolSize="524288"      maxBufferSize="6553600"      maxConnections="100"      maxReceivedMessageSize="6553600" >    </binding>   </netTcpBinding>  </bindings> </system.serviceModel>

View Code

共享元数据的方式调用

  然后在Wcf_ProtoBufSample.ClientViaReference项目中添加对Wcf_ProtoBufSample.Service的引用并配置客户端的调用信息:

 <system.serviceModel>  <bindings>   <netTcpBinding>    <binding name="DefaultTcpBinding"      closeTimeout="00:00:30"      openTimeout="00:00:30"      receiveTimeout="00:05:00"      sendTimeout="00:50:00"      transactionFlow="true"      transferMode="Buffered"      listenBacklog="100"      maxBufferPoolSize="524288"      maxBufferSize="6553600"      maxConnections="100"      maxReceivedMessageSize="6553600" >    </binding>   </netTcpBinding>  </bindings>  <behaviors>   <endpointBehaviors>    <behavior name="protoEndpointBehavior">     <protobuf/>    </behavior>   </endpointBehaviors>  </behaviors>  <extensions>   <behaviorExtensions>    <add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67"/>   </behaviorExtensions>  </extensions>  <client>   <endpoint address="net.tcp://127.0.0.1:6978/GreeterService"      binding="netTcpBinding"     bindingConfiguration="DefaultTcpBinding"      contract="Wcf_ProtoBufSample.Service.IGreeterService"     behaviorConfiguration="protoEndpointBehavior"     name="GreeterService">   </endpoint>  </client> </system.serviceModel>

View Code

  简单测试一下:

ChannelFactory<IGreeterService> factory = new ChannelFactory<IGreeterService>("GreeterService");      IGreeterService client = factory.CreateChannel();      var res = client.Get(new Request() {Name = "liam",Age = 18});      Console.WriteLine(res.GreetInfo);      Console.ReadKey();

View Code

通过添加服务引用或者WcfUtil

   添加服务引用才是我们的最爱,简单快捷,易于维护:

在Wcf_ProtoBufSample.ClientViaMetaData中右键添加服务引用,这里我公开的地址是:net.tcp://127.0.0.1:6976/mex,

  拿过来直接用肯定是不行的,毕竟我们已经修改了默认的序列化器,所以在配置中添加对ProtoBuf的配置信息,所以还是需要在配置中引用ProtoBuf的配置的:

 <system.serviceModel>    <bindings>      <netTcpBinding>        <binding name="NetTcpBinding_IGreeterService" />      </netTcpBinding>    </bindings>    <client>      <endpoint address="net.tcp://127.0.0.1:6978/GreeterService"        behaviorConfiguration="protoEndpointBehavior"       binding="netTcpBinding"       bindingConfiguration="NetTcpBinding_IGreeterService"        contract="ServiceReference.IGreeterService"       name="NetTcpBinding_IGreeterService">        <identity>          <userPrincipalName value="DESKTOP-078UA43\admin" />        </identity>      </endpoint>    </client>    <behaviors>   <serviceBehaviors>    <behavior name="GreeterServiceBehavior">     <serviceMetadata/>     <serviceDebug includeExceptionDetailInFaults="true"/>    </behavior>   </serviceBehaviors>   <endpointBehaviors>    <behavior name="protoEndpointBehavior">     <protobuf/>    </behavior>   </endpointBehaviors>  </behaviors>  <extensions>   <behaviorExtensions>    <add name="protobuf" type="ProtoBuf.ServiceModel.ProtoBehaviorExtension, protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67"/>   </behaviorExtensions>  </extensions>  </system.serviceModel>

View Code

  简单调用一下进行测试:

GreeterServiceClient client=new GreeterServiceClient("NetTcpBinding_IGreeterService");      var res = client.Get(new Request() { Name = "liam", Age = 18 });      Console.WriteLine(res.GreetInfo);

View Code

  运行后发现报错了!

  细致一些就不难发现,尽管我们的代理类生成的很简单快捷,但公开元数据的描述不会包含ProtoBuf特性的描述,所以此时我们定义的 [DataMember(Order = 0)]的Order属性此时就要发光发热了!接下来要修改的就是生成的代理类,添加ProtoBuf的序列号特性,在类上标注ProtoContract特性在属性上标注ProtoMember的特性,而且可以看着Order的顺序就行标注:

ProtoBuf的序列化是有顺序的,所以为了保证与服务端一致,此处需要谨慎(此处需要注意,更新服务引用小心自己定义的属性被覆盖)

简单测试:

Over!

(备注:貌似这么做比较复杂,毕竟开源的项目还是挺多的:https://github.com/maingi4/ProtoBuf.Services)