thrift的IDL,相当于一个钥匙。而thrift传输过程,相当于从两个房间之间的传输数据。

图-1

(因为Thrift采用了C/S模型,不支持双向通信:client只能远程调用server端的RPC接口,但client端则没有RPC供server端调用,这意味着,client端能够主动与server端通信,但server端不能主动与client端通信而只能被动地对client端的请求作出应答。所以把上图-1中的箭头,画为单向箭头更为直观)基于上图,Thrift的IDL文件的作用可以描述为,从房间A要传递一批数据给房间B,把数据装在箱子里,锁上锁,然后通过Transport Channel把带锁的箱子给房间B,而Thrift IDL就是一套锁跟钥匙。房间A跟房间B都有同样的一个thrift IDL文件,有了这个文件就可以生成序列化跟反序列化的代码,就像锁跟钥匙一样。而一旦没有了Thrift IDL文件,房间A无法将数据序列化好锁紧箱子,房间B没有钥匙打开用箱子锁好传输过来的数据。因此,IDL文件的作用就在此。

一、为什么要用Thrift序列化, 不用纯文本的协议

我能想到的用thrift提供的thrift binary protocol而不用json、xml等纯文本序列化协议的几个好处如下:

  • 序列化后的体积小, 省流量
  • 序列化、反序列化的速度更快,提高性能
  • 兼容性好,一些接口,涉及多种语言的int32、int64等等跟语言、机器、平台有关, 用纯文本可能有兼容性的问题
  • 结合thrift transport技术,可以RPC精准地传递对象,而纯文本协议大多只能传递数据,而不能完完全全传递对象

关于以上几点我个人认为的好处,下面作一下简单解答

1.1 序列化后的体积小, 省流量

给出测试的IDL Services_A.thrift内容如下,定义了一些数据格式,这个结构体数据复杂度一般,有list、也有list对象、也有一些基本的struct等等。

  1. namespace php Services.test.A
  2. namespace java Services.tets.A
  3. struct student{
  4. 1:required string studentName, #学生姓名
  5. 2:required string sex, #性别
  6. 3:required i64 age, #学生年龄
  7. }
  8. struct banji{
  9. 1:required string banjiName, #班级名称
  10. 2:required list<student> allStudents, #所有学生
  11. }
  12. struct school {
  13. 1:required string schoolName,
  14. 2:required i64 age,
  15. 3:required list<string> zhuanye, #所有专业
  16. 4:required list<banji> allBanji, #所有班级
  17. }

分别把Services_A.thrift 序列化为json、跟thrift.bin文件,序列化到文件中。对比文件大小。

这里用php写了一个例子, 代码如下:

  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: dengpan
  5. * Date: 16/4/21
  6. * Time: 12:39
  7. */
  8. ini_set('memory_limit','10240M');
  9. require_once __DIR__ . "/Thrift/ClassLoader/ThriftClassLoader.php";
  10. use Thrift\ClassLoader\ThriftClassLoader;
  11. use Thrift\Protocol\TBinaryProtocol;
  12. use Thrift\Transport\TSocket;
  13. use Thrift\Transport\TFramedTransport;
  14. use Thrift\Transport\TPhpStream;
  15. use Thrift\Transport\TPhpStreamMy;
  16. use Thrift\Transport\TBufferedTransport;
  17. $loader = new ThriftClassLoader();
  18. $loader->registerNamespace('Thrift', __DIR__);
  19. $loader->registerNamespace('Services', __DIR__);
  20. $loader->registerDefinition('Services', __DIR__);
  21. $loader->register();
  22. require "Types.php";
  23. $school = [];
  24. $school['schoolName'] = "hahhaha";
  25. $school['age'] = 60;
  26. $school['zhuanye'] = [
  27. '专业1',
  28. '专业2',
  29. '专业3',
  30. ];
  31. $nameArr = ["张三", "李四", "王五", "王菲", "张韶涵", "王祖贤", "范冰冰", "新垣结衣", "詹姆斯", "诺维茨基"];
  32. $sexArr = ["男", "女"];
  33. $allBanji = [];
  34. for($i = 0; $i < 1000; $i ++)
  35. {
  36. $banji = [];
  37. $banji['banjiName'] = "计算机" + $i + "班";
  38. for($j = 0; $j < 1000; $j ++) {
  39. $banji['allStudents'][] = new student(
  40. [
  41. 'studentName' => $nameArr[rand(0, count($nameArr) - 1)],
  42. 'sex' => $sexArr[rand(0, count($sexArr) - 1)],
  43. 'age' => rand(0, 6) + 18,
  44. ]
  45. );
  46. }
  47. $allBanji[] = new banji($banji);
  48. }
  49. $school['allBanji'] = $allBanji;
  50. $sc = new school($school);
  51. $str = json_encode($school);
  52. file_put_contents("/tmp/json_1", $str);
  53. $transport = new TBufferedTransport(new TPhpStreamMy(null,'/tmp/thrift_1.bin'),1024,1024);$sc->write(newTBinaryProtocol($transport,true,true));

最终的结果, json文件跟thrift binary文件大小如下图:

thrift binary file是json文件大小的 0.63。为什么文件小这么多,我认为有以下几个原因,① json是文本的,而thrift是二进制的,文本文件跟二进制文件的区别,可以参考我的另一篇文章, 二进制文件跟普通文本文件的区别 。② thrift序列化过程中,由于IDL在client、servo端都有一套,所以没有传输一些字段比如field name等等,只需要传递field id, 这样也是一些节省字节的方法。另外,提醒一下,序列化跟压缩不是一回事,比如上面的文件压缩为xz看看大小如下图:

压缩后体积变为原来的3.3%、4.4%。可以先序列化再压缩传输,压缩的compress-level要指定合理,不然压缩跟解压缩占据系统资源而且耗时大。

1.2序列化、反序列化的速度更快,提高性能

下面给出一个序列化反序列化测试

分别用① java Thrift binary protocol跟java fastjson库去序列以上对应格式为json, ②用C++ Thrift binary protocol与c++ jsoncpp序列化,对比速度,

java 测试代码如下:

  1. public class ThriftDataWrite3 {
  2. private static final Random sRandom = new Random();
  3. public static void main(String[] args) throws IOException, TException{
  4. //构造school对象
  5. String[] nameArr = {"张三", "李四", "王五", "赵6", "王祖贤", "赵敏", "漩涡鸣人", "诺维茨基", "邓肯", "克莱尔丹尼斯", "长门", "弥彦", "威少"};
  6. int nameArrLength = nameArr.length;
  7. school sc = new school();
  8. sc.setSchoolName("哈哈哈哈哈哈");
  9. sc.setAge(12);
  10. List<String> l = new ArrayList<>();
  11. for (int i = 0; i < 100; i++) {
  12. l.add("专业" + i);
  13. }
  14. sc.setZhuanye(l);
  15. List<banji> allBanji = new ArrayList<banji>();
  16. for (int i = 0; i < 1000; i++) {
  17. banji bj = new banji();
  18. bj.setBanjiName("班级" + i);
  19. List allStuents = new ArrayList<student>();
  20. for (int j = 0; j < 1000; j ++) {
  21. allStuents.add(
  22. new student(
  23. nameArr[sRandom.nextInt(nameArrLength)],
  24. ((sRandom.nextInt(2) == 0) ? "男" : "女"),
  25. (sRandom.nextInt(10) + 18)
  26. )
  27. );
  28. }
  29. bj.setAllStudents(allStuents);
  30. allBanji.add(bj);
  31. }
  32. sc.setAllBanji(allBanji);
  33. //①序列化为thrift binary protocol
  34. final long startTime = System.currentTimeMillis();
  35. TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());
  36. for (int i = 0; i < 200; i++) {
  37. byte[] bytes = serializer.serialize(sc);
  38. //serializer.toString(sc);
  39. }
  40. final long endTime = System.currentTimeMillis();
  41. System.out.println("thrift序列化200次时间为" + (endTime - startTime) + "ms");
  42. //②序列化为json
  43. final long endTime7 = System.currentTimeMillis();
  44. for (int i = 0; i < 200; i++) {
  45. JSON.toJSONString(sc);
  46. }
  47. final long endTime2 = System.currentTimeMillis();
  48. System.out.println("json序列化200次时间为" + (endTime2 - endTime7) + "ms");
  49. //准备待序列化的数据
  50. byte[] bytes = serializer.serialize(sc);
  51. String jsonStr = JSON.toJSONString(sc);
  52. //③反序列thrift binary data
  53. final long endTime3 = System.currentTimeMillis();
  54. TDeserializer tDeserializer = new TDeserializer();
  55. for (int i = 0; i < 200; i++) {
  56. school sc1 = new school();
  57. tDeserializer.deserialize(sc1, bytes);
  58. // System.out.println(sc1.toString());
  59. }
  60. final long endTime4 = System.currentTimeMillis();
  61. System.out.println("thrift反序列化200次时间为" + (endTime4 - endTime3) + "ms");
  62. //④反序列化json
  63. final long endTime5 = System.currentTimeMillis();
  64. for (int i = 0; i < 200; i++) {
  65. JSON.parseObject(jsonStr, sc.getClass());
  66. // System.out.println(JSON.parseObject(jsonStr, sc.getClass()).toString());
  67. }
  68. final long endTime6 = System.currentTimeMillis();
  69. System.out.println("json反序列化200次时间为" + (endTime6 - endTime5) + "ms");
  70. }
  71. }

java的序列化thrift、json,反序列化thrift跟json的结果为:

thrift序列化200次时间为82482ms

json序列化200次时间为167954ms

thrift反序列化200次时间为42919ms

json反序列化200次时间为207896ms

其中可以看出java中thrift的序列化速度是fastjson 序列化json的2倍多,thrift反序列化的速度是fastjson反序列化json的接近5倍。

再看C++测试同样的Case, C++代码如下:

①  头文件thriftSerializeTest.h

  1. //
  2. // Created by 邓攀邓攀 on 16/5/11.
  3. //
  4. #ifndef UNTITLED_THRIFTSERIALIZETEST_H
  5. #define UNTITLED_THRIFTSERIALIZETEST_H
  6. //my own add header start
  7. #include <iostream>
  8. #include <vector>
  9. #include <string>
  10. #include <fstream>
  11. #include <sstream>
  12. #include "service_a_types.h"
  13. //my own add header end
  14. using namespace std;
  15. class thriftSerializeTest {
  16. public:
  17. static school initializeSchool();
  18. static void jsonSerialize(school sc, string* str, int* time);
  19. static void thriftBinarySerialize(school sc,string* str, int* time);
  20. static void jsonDeserialize(string str, int* time);
  21. static void thriftBinaryDeserialize(string str, int* time);
  22. };
  23. #endif //UNTITLED_THRIFTSERIALIZETEST_H

② cpp 文件

  1. //
  2. // Created by 邓攀邓攀 on 16/5/11.
  3. //
  4. #include "thriftSerializeTest.h"
  5. #include "service_a_types.h"
  6. #include <sstream>
  7. #include <sys/time.h>
  8. #include "thrift/transport/TBufferTransports.h"
  9. #include "thrift/protocol/TBinaryProtocol.h"
  10. #include "json/json.h"
  11. #define MAX_NUM 200
  12. using apache::thrift::protocol::TBinaryProtocol;
  13. using apache::thrift::transport::TMemoryBuffer;
  14. school thriftSerializeTest::initializeSchool()
  15. {
  16. school sc;
  17. vector<string> nameArr{"张三", "李四", "王五", "赵6", "王祖贤", "赵敏", "漩涡鸣人", "诺维茨基", "邓肯", "克莱尔丹尼斯", "长门", "弥彦", "威少"};
  18. int nameArrLength = nameArr.size();
  19. sc.__set_age(12);
  20. sc.__set_schoolName("哈哈哈哈哈哈");
  21. vector<string> v;
  22. for (int i = 0; i < 100; ++i) {
  23. std::ostringstream oss;
  24. oss << i;
  25. v.push_back("专业" + oss.str());
  26. }
  27. sc.__set_zhuanye(v);
  28. vector<banji> allBanji;
  29. for (int j = 0; j < 1000; ++j) {
  30. banji bj;
  31. std::ostringstream oss;
  32. oss << j;
  33. bj.__set_banjiName("班级" + oss.str());
  34. vector<student> allStudents;
  35. for (int i = 0; i < 1000; ++i) {
  36. student student1;
  37. student1.__set_age(18 + (rand() % 10));
  38. student1.__set_sex((rand() % 2) ? "男" : "女");
  39. student1.__set_studentName(nameArr.at(rand() % nameArr.size()));
  40. allStudents.push_back(student1);
  41. }
  42. bj.__set_allStudents(allStudents);
  43. allBanji.push_back(bj);
  44. }
  45. sc.__set_allBanji(allBanji);
  46. return sc;
  47. }
  48. void thriftSerializeTest::jsonSerialize(school sc, string *str, int *time) {
  49. Json::Value root;
  50. root["studentName"] = Json::Value(sc.schoolName);
  51. root["age"] = Json::Value((int)sc.age);
  52. for(auto v : sc.zhuanye)
  53. {
  54. root["zhuanye"].append(v);
  55. }
  56. for(auto v: sc.allBanji)
  57. {
  58. Json::Value banji;
  59. banji["banjiName"] = Json::Value(v.banjiName);
  60. for(auto k : v.allStudents)
  61. {
  62. Json::Value student;
  63. student["studentName"] = Json::Value(k.studentName);
  64. student["age"] = Json::Value((int)k.age);
  65. student["sex"] = Json::Value(k.sex);
  66. banji["allStudents"].append(student);
  67. }
  68. root["allBanji"].append(banji);
  69. }
  70. Json::FastWriter fw;
  71. struct timeval start, end;
  72. gettimeofday(&start, NULL);
  73. for (int i = 0; i < MAX_NUM; ++i) {
  74. fw.write(root);
  75. }
  76. gettimeofday(&end, NULL);
  77. int t = 1000*(end.tv_sec - start.tv_sec);
  78. *time = t;
  79. *str = fw.write(root);
  80. }
  81. void thriftSerializeTest::thriftBinarySerialize(school sc,string *str, int *time) {
  82. struct timeval start, end;
  83. gettimeofday(&start, NULL);
  84. for (int i = 0; i < MAX_NUM; ++i) {
  85. boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
  86. boost::shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(buffer));
  87. sc.write((binaryProtcol.get()));
  88. buffer->getBufferAsString();
  89. }
  90. gettimeofday(&end, NULL);
  91. int t = 1000*(end.tv_sec - start.tv_sec);
  92. *time = t;
  93. boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
  94. boost::shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(buffer));
  95. sc.write((binaryProtcol.get()));
  96. *str = buffer->getBufferAsString();
  97. }
  98. void thriftSerializeTest::jsonDeserialize(string str, int *time) {
  99. Json::Reader reader;
  100. Json::Value root;
  101. int num = MAX_NUM;
  102. struct timeval start, end;
  103. gettimeofday(&start, NULL);
  104. while (num > 0) {
  105. //确保成功解析
  106. if (reader.parse(str.c_str(), root)) {
  107. num --;
  108. }
  109. }
  110. gettimeofday(&end, NULL);
  111. int t = 1000*(end.tv_sec - start.tv_sec);
  112. *time = t;
  113. }
  114. void thriftSerializeTest::thriftBinaryDeserialize(string str, int *time) {
  115. struct timeval start, end;
  116. gettimeofday(&start, NULL);
  117. boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
  118. boost::shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(buffer));
  119. school sc;
  120. for (int i = 0; i < MAX_NUM; ++i) {
  121. buffer->resetBuffer((uint8_t*)str.data(), str.length());
  122. sc.read(binaryProtcol.get());
  123. }
  124. gettimeofday(&end, NULL);
  125. int t = 1000*(end.tv_sec - start.tv_sec);
  126. *time = t;
  127. }
  128. int main() {
  129. //获取school对象
  130. school sc = thriftSerializeTest::initializeSchool();
  131. string thriftStr, jsonStr;
  132. int time;
  133. //①序列化为thrift binary protocol
  134. thriftSerializeTest::thriftBinarySerialize(sc, &thriftStr, &time);
  135. cout << "thrift序列化" << MAX_NUM << "次时间为" << time << "ms\n";
  136. //②反序列thrift binary data
  137. thriftSerializeTest::thriftBinaryDeserialize(thriftStr, &time);
  138. cout << "thrift反序列化" << MAX_NUM << "次时间为" << time << "ms\n";
  139. //③jsoncpp序列化为json
  140. thriftSerializeTest::jsonSerialize(sc, &jsonStr, &time);
  141. cout << "jsoncpp序列化json" << MAX_NUM << "次时间为" << time << "ms\n";
  142. //④jsoncpp反序列化json
  143. thriftSerializeTest::jsonDeserialize(jsonStr, &time);
  144. cout << "jsoncpp反序列化json" << MAX_NUM << "次时间为" << time << "ms\n";
  145. return 0;
  146. }

编译cpp的命令为:

g++ service_a_constants.cpp service_a_types.cpp  thriftSerializeTest.cpp -std=c++11 -I/home/dengpan/opt/thrift-0.9.3/include -L/home/dengpan/opt/thrift-0.9.3/lib64 -lthrift -ljsoncpp -O3

C++代码测试结果为:

thrift序列化200次时间为23000ms

thrift反序列化200次时间为48000ms

jsoncpp序列化json200次时间为330000ms

jsoncpp反序列化json200次时间为494000ms

其中可以看出c++中thrift的序列化速度是jsoncpp 序列化json的14.3倍,thrift反序列化速度是jsoncpp反序列化json的10.2倍。其中c++序列thrift比java快,c++ jsoncpp严重不如java的fastjson,可能选取更好的c++ json库,可能结果好看点,但是java的fastjson还是很优秀的,用了大量的ASM写法,应该比java、c++绝大多数的json库要好。具体的,有空看看其他的c++ json库来测试一下。读者有更好的库,也可以推荐一下。

thrift对比json,流量更小、序列化反序列化更快,甚至快上5-10倍,这两个特点,在移动端跟实时性要求很高的游戏上,显得非常重要,也吸引人们在这些场景去使用像thrift这样的RPC、序列化的工具。

1.3兼容性好

Thrift Types定义了一些基本的Base Type,分别在源代码各个语言中都有一一映射。但是每个语言中,不是所有的定义的类型都支持,Thrift的一些绝大多数语言都支持的base Type有void、bool、i16、i32、i64、double、string。然后thrift通过一一映射,将IDL里面的base Type与各种编程语言里面做了对应,这样可以保证兼容性。而且不会存在java提供的一个接口,一个json字段是i64的,到了C++调用http接口的时候,拿int去取这个字段,丢失了数字的高位。这种情况在跨语言调用,由于每个语言的int、long、short的位数与范围都不同,开发者涉及多语言需要对每个语言的类型范围、位数很清楚才可以避免这样的极端情况的丢失数据,而用了Thrift就不用担心了,它已经帮我们映射好了一些基准类型。后面的thrift code generator也是生成基于TBase类型的对象,用到的也都是thrift base types。关于thrift base type可以参考官方文档: Thrift Types 。具体的每个语言支持哪些Types定义,可以看源代码,比如thrift-src中,看到lib/rb/ext/thrift_native.c 中TType constants相关的为:

看到thrift跟ruby的类型支持情况,没看到binary, 说明ruby不支持binary类型。c++支持thrift的binary类型。其实基准类型,已经足够使用了,建议不要使用不属于i16、i32、i64、double、string、list、map之外的,非通用的TType,不然可能没事找事,碰到一些兼容问题。

1.4可传递对象

由于IDL的存在,可以在IDL里面定义一些表示层级关系,数据结构,把我们要传递的对象,翻译成IDL里面的struct, 然后再把IDL编译成对应文件,这样就可以传递对象了。json等文本协议,会丢失部分信息。比如php里面$a =[“name” => 123], json_encode后,跟 $a= new stdClass();$a->name = 123; json_encode之后,是一样的。一个是数组,一个是对象。类似的这种序列化中类型丢失或者改变的,其实还有其他的例子。要我们小心的去使用。(可能这个例子举得并不充分,因为php里面数组太灵活了,可以干绝大多数事情)。而Thrift,只要我们定义好IDL,就可以放心的去传递对象了。

二、序列化方法

任何一个Struct,Thrift code generator都为它生成了一个对应的class,该类都包含write和read方法,write就是serialie过程, read方法就是unserialize过程。由于Thrift是连带Client调用Service的代码整套生成的,因此想单独拿Thrift序列化一个对象官方没给什么例子,不过各种语言把struct序列化为binary的方法大同小异。我这里研究了下各种语言怎么把对象单独序列化为string,这里一并贴出来。

①   C++ 序列thrift对象为string

boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());boost::shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(buffer));

youThriftObj.write((binaryProtcol.get()));buffer->getBufferAsString();

② java序列化thrift对象为string

TSerializer serializer = new TSerializer(new TBinaryProtocol.Factory());serializer.toString(bb); //就是序列后之后的string

③ php序列化thrift对象为string

$memBuffer = new \Thrift\Transport\TMemoryBuffer();

$protocol = new TBinaryProtocol($memBuffer);

$bbb->write($protocol);

$str = $memBuffer->getBuffer();

④ php序列化thrift对象到File

由于php中,没有TFileTransport, 因此改写了一下TPhpStream, 改成TPhpStreamMy,这样可以比较方便序列化到文件跟从文件中反序列化,这里也一并给出, TPhpStreamMy的github gist地址 。放到php Thrift lib的Thrift/Transport/TPhpStreamMy.php位置。

序列化thrift对象到file

$transport = new TBufferedTransport(new TPhpStreamMy(null, ‘your-thrift-binary-file-path’), 1024, 1024);$yourThriftObj->write(new TBinaryProtocol($transport, true, true));

从文件中读取thrift对象

$transport = new TBufferedTransport(new TPhpStreamMy(‘your-thrift-binary-file-path’, null), 1024, 1024);$yourThriftObj->read(new TBinaryProtocol($transport));

其他语言的thrift序列化过程,也是类似步骤。

三、反序列化方法

反序列化方法跟序列化方法步骤类似,主要是把write操作改为read操作即可,下面给出一些语言的反序列化方法:

① C++ 反序列string为Object

boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());

boost::shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(buffer));

buffer->resetBuffer((uint8_t*)str.data(), str.length()); //str为thrift对象序列化之后的string

yourThriftObj.read(binaryProtcol.get());

②  java反序列byte[]为Object

TDeserializer tDeserializer = new TDeserializer();tDeserializer.deserialize(yourThriftObj, bytes);  //bytes等于thrift对象序列化之后的byte[]

③ php反序列化string为Object

$memBuffer = new \Thrift\Transport\TMemoryBuffer($str); //str为thrift对象序列化之后的string

$protocol = new TBinaryProtocol($memBuffer);

$yourThriftObj->read($protocol);

其他语言的thrift反序列化过程,也是类似步骤。

四、序列化代码解读-字节探究

这里用一个更简单的thrift service完整例子,用c++实现thrift的THttpServer, php实现thrift的THttpClient, 用wireshark抓包,看看整个通信过程,与字节发包的情况。

示例thrift/service_b.thrift IDL如下:

  1. struct student {
  2. 1:optional string name,
  3. 2:optional i64 age,
  4. }
  5. service My_Services {
  6. student getStudent(1:student st);
  7. }

C++实现thrift THttpServer代码如下:

  1. // This autogenerated skeleton file illustrates how to build a server.
  2. // You should copy it to another filename to avoid overwriting it.
  3. #include "My_Services.h"
  4. #include <thrift/protocol/TBinaryProtocol.h>
  5. #include <thrift/server/TSimpleServer.h>
  6. #include <thrift/transport/TServerSocket.h>
  7. #include <thrift/transport/TBufferTransports.h>
  8. #include <thrift/transport/THttpServer.h>
  9. #include <thrift/server/TNonblockingServer.h>
  10. using namespace ::apache::thrift;
  11. using namespace ::apache::thrift::protocol;
  12. using namespace ::apache::thrift::transport;
  13. using namespace ::apache::thrift::server;
  14. using boost::shared_ptr;
  15. class My_ServicesHandler : virtual public My_ServicesIf {
  16. public:
  17. My_ServicesHandler() {
  18. // Your initialization goes here
  19. }
  20. void getStudent(student& _return, const student& st) {
  21. // Your implementation goes here
  22. _return.__set_age(st.age * 2);
  23. _return.__set_name(st.name + st.name + "哈哈哈哈");
  24. printf("getStudents \n");
  25. }
  26. };
  27. int main(int argc, char **argv) {
  28. int port = 9090;
  29. shared_ptr<My_ServicesHandler> handler(new My_ServicesHandler());
  30. shared_ptr<TProcessor> processor(new My_ServicesProcessor(handler));
  31. shared_ptr<TServerTransport> serverTransport(new TServerSocket("127.0.0.1", port));
  32. shared_ptr<TTransportFactory> transportFactory(new THttpServerTransportFactory());
  33. shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
  34. TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  35. server.serve();
  36. return 0;
  37. }

server实现很简单,就是接受student对象,然后把student对象中的student.age = student.age * 2, student.name = student.name + student.name + “哈哈哈哈”,然后返回这个student对象。

php实现Php THttpClient如下:

  1. <?php
  2. // 引入客户端文件
  3. require_once __DIR__ . "/../Thrift/ClassLoader/ThriftClassLoader.php";
  4. require "My_Services.php";
  5. require "Types.php";
  6. use Thrift\ClassLoader\ThriftClassLoader;
  7. use Thrift\Protocol\TBinaryProtocol;
  8. use Thrift\Transport\TSocket;
  9. use Thrift\Transport\TFramedTransport;
  10. $loader = new ThriftClassLoader();
  11. $loader->registerNamespace('Thrift', __DIR__ . "/../");
  12. $loader->registerNamespace('Swoole', __DIR__ . "/../");
  13. $loader->registerNamespace('Services', __DIR__ . "/../");
  14. $loader->registerDefinition('Services', __DIR__ . "/../");
  15. $loader->register();
  16. $transport = new Thrift\Transport\THttpClient("127.0.0.1", 9090);
  17. $protocol = new TBinaryProtocol($transport);
  18. $client = new My_ServicesClient($protocol);
  19. $transport->open();
  20. $res = $client->getStudent(new student(
  21. [
  22. 'name' => '邓攀',
  23. 'age' => 1222,
  24. ]
  25. ));
  26. var_dump($res);
  27. $transport->close();
  28. $transport->close();

用wireshark抓包查看http通信过程,注意这个是本地127.0.0.1通信的,要在wireshark抓包指定loopback监听

然后在wireshark中指定过滤器

http.content_type == “application/x-thrift” && http.request.method==”POST”

监听到请求,

可以看到监听到的请求,Content-Length是69B。为了方便跟踪,follow 这个tcp流,

可以看到http ressquest发送了226字节,http response收到了257字节。我把发送的数据、接收的数据,去除http头,就是纯二进制数据,写进了文件/tmp/thttp_request、/tmp/thttp_response中。文件大小与hexdump的文件值如下:

下面来分析下这个文件的字节内容。以thttp_response文件为例,我们收到了69个字节,解析出来了name:邓攀邓攀哈哈哈哈,age:2444的数据。

五、提高一些脚本语言中的序列化性能

一些脚本语言的序列化性能不太好,再特别注意性能的场景下,建议可以用C/C++实现thrift的序列化,然后写成语言的一个模块、module的形式,给脚本语言去调用,这样可以极大的提高性能。也能利用到thrift的优点。

参考文章:

  1. 让Thrift支持双向通信--董的博客
  2. Thrift: The Missing Guide
  3. Thrift 官方文档

关于thrift的一些探索——thrift序列化技术的更多相关文章

  1. Thrift RPC实战(三) thrift序列化揭秘

    本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...

  2. Java序列化技术与Protobuff

    http://www.cnblogs.com/fangfan/p/4094175.html http://www.cnblogs.com/fangfan/p/4094175.html 前言: Java ...

  3. .net中对象序列化技术浅谈

    .net中对象序列化技术浅谈 2009-03-11 阅读2756评论2 序列化是将对象状态转换为可保持或传输的格式的过程.与序列化相对的是反序列化,它将流转换为对象.这两个过程结合起来,可以轻松地存储 ...

  4. [.NET]使用十年股价对比各种序列化技术

    1. 前言 上一家公司有搞股票,当时很任性地直接从服务器读取一个股票10年份的股价(还有各种指标)在客户端的图表上显示,而且因为是桌面客户端,传输的数据也是简单粗暴地使用Soap序列化.获取报价的接口 ...

  5. Java序列化技术即将被废除!!!

    我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术.Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制数据保存到磁盘或者 ...

  6. 基于序列化技术(Protobuf)的socket文件传输

    好像好久都没更博文了,没办法,最近各种倒霉事情,搞到最近真的没什么心情,希望之后能够转运吧. 言归正传,这次我要做的是基于序列化技术的socket文件传输来无聊练一下手. 一.socket文件传输 之 ...

  7. 揭开DRF序列化技术的神秘面纱

    在RESTful API中,接口返回的是JSON,JSON的内容对应的是数据库中的数据,DRF是通过序列化(Serialization)的技术,把数据模型转换为JSON的,反之,叫做反序列化(dese ...

  8. 【Java技术专题】「性能优化系列」针对Java对象压缩及序列化技术的探索之路

    序列化和反序列化 序列化就是指把对象转换为字节码: 对象传递和保存时,保证对象的完整性和可传递性.把对象转换为有字节码,以便在网络上传输或保存在本地文件中: 反序列化就是指把字节码恢复为对象: 根据字 ...

  9. Thrift教程初级篇——thrift安装环境变量配置第一个实例

    前言: 因为项目需要跨语言,c++客户端,web服务端,远程调用等需求,所以用到了RPC框架Thrift,刚开始有点虚,第一次接触RPC框架,后来没想到Thrift开发方便上手快,而且性能和稳定性也不 ...

随机推荐

  1. 持续集成Jenkins入门【截图】

  2. git 学习之基础知识

    在前面的一个帖子中我们知道了不同的版本控制系统的不同,在这个帖子中我们会大致的了解到它们是管理各个版本的,这对我们学习 git 是很有帮助的. 对于集中式的版本控制系统来说每次的更新主要记录内容的具体 ...

  3. GITHUB一个新的项目发布

    经过一段时间的积累,写了一些代码,发现好多功能有好几个系统都在用,但是公司的开发过程中,并没有一个对通用功能提取整合普遍化的一个流程,所以就自己将在项目开发过程中遇到的一些功能提取出来,并尽量做到普适 ...

  4. Mysql远程连接配置

    Mysql远程连接配置 环境:unbuntu 16.04 最新版本的Mysql在远程连接的配置上与老版本有了一些出入,照原先的配置已经不行了,所以在这里记录一下遇到的所有新问题. 配置远程连接的步骤如 ...

  5. php的stristr()函数,查找字符

    1.用法,要传2个参数 stristr(string,search):查找并返还匹配后,剩下的部分字符串 查找过程不区分大小写,要区分大小写用 strstr(string,search)少一个字母i ...

  6. Java入门系列-16-继承

    这一篇文章教给新手学会使用继承,及理解继承的概念.掌握访问修饰符.掌握 final 关键字的用法. 继承 为什么要使用继承 首先我们先看一下这两个类: public class Teacher { p ...

  7. PHP之数组和函数的基本教程

    [PHP数组的分类] 按照下标的不同,PHP数组分为关联数组与索引数组 索引数组:下标从0开始,依次增长: 关联数组:下标为字符串格式,每个下标字符串与数字的值一一关联对应(有点像对象的键值对) [关 ...

  8. [android] socket在手机上的应用

    1.手机助手 1.1 USB链接 可以读取手机的PID和VID,确定唯一的设备,可以给手机安装对应的驱动等 socket在固定端口通信 1.2 WIFI链接 pc在电脑在整个网段发送UDP数据包,手机 ...

  9. SPOJ2666 QTREE4

    我是萌萌的传送门 我是另一个萌萌的传送门 一道树分治……简直恶心死了……我在调代码的时候只想说:我*************************************************…… ...

  10. 【代码笔记】Java常识性基础补充(三)——Java的API及Object类、正则表达式、getTime()方法、DateFormat类、Calendar类

    1.0 Java 的API(API: Application(应用) Programming(程序) Interface(接口)) 2.0 Java API就是JDK中提供给我们使用的类,这些类将底层 ...