Wednesday, March 30, 2011

Protocol Buffers and WCF

WCF performance has many aspects. In the previous series I explored how using GZip/Deflate compression can increase performance in areas with low network latency. However, the penalty is that the CPU utilization is much higher. Therefore, it does not apply to many people's situations.

Instead of compressing messages after they are built, it would be better if we could build smaller messages. Google has a format for serializing objects that is lightweight and fast called Protocol Buffers. The specification for Protocol Buffers is available publicly and there is a .Net implementation available called protobuf-net.

Similar lightweight serialization formats are used by other large companies and none of them are meant to be considered as standards for information interchange. But neither is Microsoft's binary serialization format. Binary serialization in WCF is meant for cases where you have WCF at both ends of the call. However, binary serialization is not as lightweight as Protocol Buffers because of one very important reason: Microsoft's binary serialization format uses an XML infoset. Think of it more as a binary XML format than anything else.

Luckily, the protobuf-net implementation is already set up to work with WCF. The protocol buffers serializer basically replaces the DataContractSerializer used by WCF. For this implementation, we simply need to add on the custom attributes protobuf-net requires to our existing DataContracts.

[DataContract]
[ProtoContract]
public partial class Order
{
    [DataMember(Order = 0)]
    [ProtoMember(1)]
    public int CustomerID;
    [DataMember(Order = 1)]
    [ProtoMember(2)]
    public string ShippingAddress1;
...
[DataContract]
[ProtoContract]
public partial class OrderLine
{
    [DataMember(Order = 0)]
    [ProtoMember(1)]
    public int ItemID;
    [DataMember(Order = 1)]
    [ProtoMember(2)]
    public int Quantity;
}

The above shows the Order and OrderLine classes from the previous posts. Nothing particularly weird except it does seem a bit redundant to have two sets of custom attributes that mean the same thing. The DataContract portions could be taken off if you want it to be easier to read. Obviously, there are more options available in the Proto* attributes, which is why they decided to use new attributes instead of sticking with DataContract/DataMember.

To use the protocol buffers serializer in place of the DataContractSerializer, just add the endpoint behavior:

ServiceEndpoint endpoint = this.serviceHost.AddServiceEndpoint(typeof(IOrderService), binding, "OrderService");
endpoint.Behaviors.Add(new ProtoEndpointBehavior());