Google的ProtoBuf序列化器性能的牛逼已經有目共睹了,可以把它應用到Socket通訊,隊列,Wcf中,身為dotnet程式員一邊期待著不久後Grpc對dotnet core的支持更期待著Wcf有一天能在Linux平臺上閃瞎所有人。現在簡單表述下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)