Apache Thrift和ProtoBuf一樣,都是為優化序列化而生,Thrift是一個通信框架,最初有FaceBook開發,后交由Apache管理,目前Facebook也在使用。Thrift與ProtoBuf同樣是跨平台多語言的,不過Thrift幾乎支持現下的所有流行的語言,而ProtoBuf.net是.net的移植,相比而來Thrift支持更廣。我這里做的是對Thrift序列化數據在效率做實驗
1.定義接口文件
相當於數據契約。Thrift的契約定義用了自己的一套語法(IDL),這樣做的目的是讓他可以跨語言,同一套契約可以在任何語言下使用,關於IDL方法可以去參考Thrift的白皮書。
和上一篇文章相同,有以下這樣兩個類
public class Person
{
public int Id { get; set; }]
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
用Thrift 的IDL語言定義后如下,文件保存為test.idl
struct Address
{
1: string Line1,
2: string Line2
}
struct Person
{
1: i32 Id,
2: string Name,
3: Address Address
}
2. 使用Thrift提供的windows編譯器生成文件
使用thrift-0.9.1.exe生成文件,將test.idl與thrift-0.9.1.exe放在同級目錄下,在控制台是執行命令
thrift-0.9.1 --gen csharp test.idl
文件生成在目錄下的gen-csharp文件夾中

Address.cs/** * Autogenerated by Thrift Compiler (0.9.1) * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING * @generated */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Thrift;
using Thrift.Collections;
using System.Runtime.Serialization;
using Thrift.Protocol;
using Thrift.Transport;
#if !SILVERLIGHT
[Serializable]
#endif
public partial class Address : TBase
{
private string _Line1;
private string _Line2;
public string Line1
{
get
{
return _Line1;
}
set
{
__isset.Line1 = true;
this._Line1 = value;
}
}
public string Line2
{
get
{
return _Line2;
}
set
{
__isset.Line2 = true;
this._Line2 = value;
}
}
public Isset __isset;
#if !SILVERLIGHT
[Serializable]
#endif
public struct Isset {
public bool Line1;
public bool Line2;
}
public Address() {
}
public void Read (TProtocol iprot)
{
TField field;
iprot.ReadStructBegin();
while (true)
{
field = iprot.ReadFieldBegin();
if (field.Type == TType.Stop) {
break;
}
switch (field.ID)
{
case 1:
if (field.Type == TType.String) {
Line1 = iprot.ReadString();
} else {
TProtocolUtil.Skip(iprot, field.Type);
}
break;
case 2:
if (field.Type == TType.String) {
Line2 = iprot.ReadString();
} else {
TProtocolUtil.Skip(iprot, field.Type);
}
break;
default:
TProtocolUtil.Skip(iprot, field.Type);
break;
}
iprot.ReadFieldEnd();
}
iprot.ReadStructEnd();
}
public void Write(TProtocol oprot) {
TStruct struc = new TStruct("Address");
oprot.WriteStructBegin(struc);
TField field = new TField();
if (Line1 != null && __isset.Line1) {
field.Name = "Line1";
field.Type = TType.String;
field.ID = 1;
oprot.WriteFieldBegin(field);
oprot.WriteString(Line1);
oprot.WriteFieldEnd();
}
if (Line2 != null && __isset.Line2) {
field.Name = "Line2";
field.Type = TType.String;
field.ID = 2;
oprot.WriteFieldBegin(field);
oprot.WriteString(Line2);
oprot.WriteFieldEnd();
}
oprot.WriteFieldStop();
oprot.WriteStructEnd();
}
public override string ToString() {
StringBuilder sb = new StringBuilder("Address(");
sb.Append("Line1: ");
sb.Append(Line1);
sb.Append(",Line2: ");
sb.Append(Line2);
sb.Append(")");
return sb.ToString();
}
}

Person.cs/** * Autogenerated by Thrift Compiler (0.9.1) * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING * @generated */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Thrift;
using Thrift.Collections;
using System.Runtime.Serialization;
using Thrift.Protocol;
using Thrift.Transport;
#if !SILVERLIGHT
[Serializable]
#endif
public partial class Person : TBase
{
private int _Id;
private string _Name;
private Address _Address;
public int Id
{
get
{
return _Id;
}
set
{
__isset.Id = true;
this._Id = value;
}
}
public string Name
{
get
{
return _Name;
}
set
{
__isset.Name = true;
this._Name = value;
}
}
public Address Address
{
get
{
return _Address;
}
set
{
__isset.Address = true;
this._Address = value;
}
}
public Isset __isset;
#if !SILVERLIGHT
[Serializable]
#endif
public struct Isset {
public bool Id;
public bool Name;
public bool Address;
}
public Person() {
}
public void Read (TProtocol iprot)
{
TField field;
iprot.ReadStructBegin();
while (true)
{
field = iprot.ReadFieldBegin();
if (field.Type == TType.Stop) {
break;
}
switch (field.ID)
{
case 1:
if (field.Type == TType.I32) {
Id = iprot.ReadI32();
} else {
TProtocolUtil.Skip(iprot, field.Type);
}
break;
case 2:
if (field.Type == TType.String) {
Name = iprot.ReadString();
} else {
TProtocolUtil.Skip(iprot, field.Type);
}
break;
case 3:
if (field.Type == TType.Struct) {
Address = new Address();
Address.Read(iprot);
} else {
TProtocolUtil.Skip(iprot, field.Type);
}
break;
default:
TProtocolUtil.Skip(iprot, field.Type);
break;
}
iprot.ReadFieldEnd();
}
iprot.ReadStructEnd();
}
public void Write(TProtocol oprot) {
TStruct struc = new TStruct("Person");
oprot.WriteStructBegin(struc);
TField field = new TField();
if (__isset.Id) {
field.Name = "Id";
field.Type = TType.I32;
field.ID = 1;
oprot.WriteFieldBegin(field);
oprot.WriteI32(Id);
oprot.WriteFieldEnd();
}
if (Name != null && __isset.Name) {
field.Name = "Name";
field.Type = TType.String;
field.ID = 2;
oprot.WriteFieldBegin(field);
oprot.WriteString(Name);
oprot.WriteFieldEnd();
}
if (Address != null && __isset.Address) {
field.Name = "Address";
field.Type = TType.Struct;
field.ID = 3;
oprot.WriteFieldBegin(field);
Address.Write(oprot);
oprot.WriteFieldEnd();
}
oprot.WriteFieldStop();
oprot.WriteStructEnd();
}
public override string ToString() {
StringBuilder sb = new StringBuilder("Person(");
sb.Append("Id: ");
sb.Append(Id);
sb.Append(",Name: ");
sb.Append(Name);
sb.Append(",Address: ");
sb.Append(Address== null ? "<null>" : Address.ToString());
sb.Append(")");
return sb.ToString();
}
}
3. 添加Thrift.dll
下載thrift-0.9.1.tar.gz解壓后找到對應的C#代碼編譯后就得到Thrift.dll了

4. 序列化數據
創建一個C#控制台應用程序,將生成的兩個代碼文件添加到項目中,再添加Thrift.dll引用,和上次一樣序列化同樣的1000條數據,得到的數據大小為48.7 KB (49,890 字節)
class Program
{
static void Main(string[] args)
{
Person person = new Person { Address = new Address { Line1 = "Line1", Line2 = "Line2" }, Id = 1, Name = "Name" };
using (System.IO.Stream stream = System.IO.File.Create("Person.dat"))
{
Thrift.Transport.TTransport transport = new Thrift.Transport.TStreamTransport(null,stream);
Thrift.Protocol.TProtocol protocol = new Thrift.Protocol.TBinaryProtocol(transport);
List<Person> list = new List<Person>();
for (int i = 0; i < 1000; i++)
{
Person item = new Person { Address = new Address { Line1 = "Line1", Line2 = "Line2" }, Id = i, Name = "Name" + i };
item.Write(protocol);
}
}
}
}
Demo下載
對比上次的ProtoBuf大了不少,和C#的原始二進制序列化只差5kb。當然我這次對比幾個數據也不能說明什么問題,只能做個大概了解。網絡通信是個復雜的過程。序列化數據大小直接影響網絡帶寬,序列化與反序列化的效率也直接影響着服務器資源。當數據量大小上升到不同數量級時ProtoBuf和Thrift到底誰更占上風我就法驗證了。
Thrift是個網絡通信框架,提供了多種數據序列化方式,並且支持多種語言,通過Thrift的IDL語言定義過的契約可以在所有支持的語言上使用,且與平台無關,這就是他NB的地方。從Thrift的Tutorial看來對現將通信協議遷移也不是件事。