http://dongxicheng.org/search-engine/thrift-rpc/

1. 概述

本文以C++语言为例介绍了thrift RPC的使用方法,包括对象序列化和反序列化,数据传输和信息交换等。

本文采用了一个示例进行说明,该示例主要完成传输(上报日志或者报表)功能,该示例会贯穿本文,内容涉及thrift定义,代码生成,thrift类说明,client编写方法,server编写方法等。

关于Thrift架构分析,可参考:Thrift架构介绍

关于Thrift文件编写方法,可参考:Thrift使用指南。

2. 示例描述

假设我们要使用thrift RPC完成一个数据传输任务,数据格式和PRC接口用一个thrift文件描述,具体如下:

(1) book.thrift,用于描述书籍信息的thrift接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//book.thrift,
  
namespace cpp example
  
struct Book_Info {
  
1: i32 book_id,
  
2: string book_name,
  
3: string book_author,
  
4: double book_price,
  
5: string book_publisher,
  
}

(2) rpc.thrift,client向server传输数据(上报日志或者报表)的RPC接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//rpc.thrift
  
namespace cpp example
  
include "book.thrift"
  
service BookServlet {
  
bool Sender(1: list<book.Book_Info> books);
  
oneway void Sender2(1: list<book.Book_Info> books);
  
}

说明:该thrift文件定义了一个service,它包含两个接口,server端需要实现这两个接口以对client提供服务。其中,第一个接口函数是阻塞式的,即要等待server返回值以后才能继续,另外一个声明为oneway类型(返回值为void),表明该函数是非阻塞式的,将数据发给server后不必等待返回结果,但使用该函数时,需要考虑server的承受能力,适度的调整发送频率。

3. Thrift文件与生成的代码对应关系

每个thrift文件会产生四个文件,分别为:${thrift_name}_constants.h,${thrift_name}_constants.cpp,${thrift_name}_types.h,${thrift_name}_types.cpp

对于含有service的thrift文件,会额外生成两个文件,分别为:${service_name}.h,${service_name}.cpp

对于含有service的thrift文件,会生成一个可用的server桩:${service_name}._server.skeleton.cpp

对于本文中的例子,会产生以下文件:

book_constants.h book_constants.cpp

book_types.h book_types.cpp

rpc_constants.h rpc_constants.cpp

rpc_types.h rpc_types.cpp

BookServlet.h BookServlet.cpp

BookServlet_server.skeleton.cpp

4. Thrift类介绍

Thrift代码包(位于thrift-0.6.1/lib/cpp/src)有以下几个目录:

concurrency:并发和时钟管理方面的库

processor:Processor相关类

protocal:Protocal相关类

transport:transport相关类

server:server相关类

4.1 Transport类(how is transmitted?)

负责数据传输,有以下几个可用类:

TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。

THttpTransport:采用Http传输协议进行数据传输

TSocket:采用TCP Socket进行数据传输

TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压

下面几个类主要是对上面几个类地装饰(采用了装饰模式),以提高传输效率。

TBufferedTransport:对某个Transport对象操作的数据进行buffer,即从buffer中读取数据进行传输,或者将数据直接写入buffer

TFramedTransport:同TBufferedTransport类似,也会对相关数据进行buffer,同时,它支持定长数据发送和接收。

TMemoryBuffer:从一个缓冲区中读写数据

4.2 Protocol类(what is transmitted?)

负责数据编码,主要有以下几个可用类:

TBinaryProtocol:二进制编码

TJSONProtocol:JSON编码

TCompactProtocol:密集二进制编码

TDebugProtocol:以用户易读的方式组织数据

4.3 Server类(providing service for clients)

TSimpleServer:简单的单线程服务器,主要用于测试

TThreadPoolServer:使用标准阻塞式IO的多线程服务器

TNonblockingServer:使用非阻塞式IO的多线程服务器,TFramedTransport必须使用该类型的server

5. 对象序列化和反序列化

Thrift中的Protocol负责对数据进行编码,因而可使用Protocol相关对象进行序列化和反序列化。

由于对象序列化和反序列化不设计传输相关的问题,所以,可使用TBinaryProtocol和TMemoryBuffer,具体如下:

(1) 使用thrift进行对象序列化

//对对象object进行序列化,保存到str中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename Type>
  
void Object2String(Type& object, string &str) {
  
  shared_ptr<TMemoryBuffer> membuffer(new TMemoryBuffer());
  
  shared_ptr<TProtocol> protocol(new TBinaryProtocol(membuffer));
  
  object.write(protocol.get());
  
  str.clear();
  
  str = membuffer.getBufferAsString();
  
}

(2)使用thrift进行对象反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//对str中保存的对象进行反序列化,保存到object中
  
template <typename Type>
  
void String2Object(string& buffer, Type &object) {
  
  shared_ptr<TMemoryBuffer> membuffer(new TMemoryBuffer(
  
  reinterpret_cast<uint*>(buffer.data())));
  
  shared_ptr<TProtocol> protocol(new TBinaryProtocol(membuffer));
  
  object.read(protocol.get());
  
}

6. 编写client和server

6.1 client端代码编写

Client编写的方法分为以下几个步骤:

(1) 定义TTransport,为你的client设置传输方式(如socket, http等)。

(2) 定义Protocal,使用装饰模式(Decorator设计模式)封装TTransport,为你的数据设置编码格式(如二进制格式,JSON格式等)

(3) 实例化client对象,调用服务接口。

说明:如果用户在thrift文件中定义了一个叫${server_name}的service,则会生成一个叫${server_name}Client的对象,比如,我给出的例子中,thrift会自动生成一个叫BookServletClient的类,Client端的代码编写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include " gen-cpp/BookServlet.h" //一定要包含该头文件
  
//其头文件,其他using namespace …….
  
int main(int argc, char** argv) {
  
  shared_ptr<TTransport> socket(new TSocket("localhost", 9090));
  
  shared_ptr<TTransport> transport(new TBufferedTransport(socket));
  
  shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
  
  example::BookServletClient client(protocol);
  
try {
  
  transport->open();
  
  vector<example::Book_Info> books;
  
  …...
  
  client.Sender(books);//RPC函数,调用serve端的该函数
  
  transport->close();
  
} catch (TException &tx) {
  
  printf("ERROR: %s\n", tx.what());
  
}
  
}

6.2 Server端代码编写

(1) 定义一个TProcess,这个是thrift根据用户定义的thrift文件自动生成的类

(2) 使用TServerTransport获得一个TTransport

(3) 使用TTransportFactory,可选地将原始传输转换为一个适合的应用传输(典型的是使用TBufferedTransportFactory)

(4) 使用TProtocolFactory,为TTransport创建一个输入和输出

(5) 创建TServer对象(单线程,可以使用TSimpleServer;对于多线程,用户可使用TThreadPoolServer或者TNonblockingServer),调用它的server()函数。

说明:thrift会为每一个带service的thrift文件生成一个简单的server代码(桩),在例子中,thrift会生成BookServlet_server.skeleton.cpp,用户可以在这个文件基础上实现自己的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include "gen-cpp/BookServlet.h"
  
#include <protocol/TBinaryProtocol.h>
  
#include <server/TSimpleServer.h>
  
#include <transport/TServerSocket.h>
  
#include <transport/TBufferTransports.h>
  
using namespace ::apache::thrift;
  
using namespace ::apache::thrift::protocol;
  
using namespace ::apache::thrift::transport;
  
using namespace ::apache::thrift::server;
  
using boost::shared_ptr;
  
using namespace example;
  
class BookServletHandler : virtual public BookServletIf {
  
public:
  
BookServletHandler() {
  
// Your initialization goes here
  
}
  
//用户需实现这个接口
  
bool Sender(const std::vector<example::Book_Info> & books) {
  
  // Your implementation goes here
  
  printf("Sender\n");
  
}
  
//用户需实现这个接口
  
void Sender2(const std::vector<example::Book_Info> & books) {
  
  // Your implementation goes here
  
  printf("Sender2\n");
  
}
  
};
  
int main(int argc, char **argv) {
  
  int port = 9090;
  
  shared_ptr<BookServletHandler> handler(new BookServletHandler());
  
  shared_ptr<TProcessor> processor(new BookServletProcessor(handler));
  
  shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  
  shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  
  shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
  
  TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  
  server.serve();
  
  return 0;
  
}

7. 总结

至此,关于thrift框架的三篇文章已经全部完成,包括:

(1) Thrift框架介绍: Thrift框架介绍

(2) Thrift文件编写方法: Thrift使用指南

(3) Thrift RPC使用方法:利用Thrift RPC编写程序

与thrift类似的开源RPC框架还有google的protocal buffer,它虽然支持的语言比较少,但效率更高,因而受到越来越多的关注。

由于thrift开源时间很早,经受了时间的验证,因而许多系统更愿意采用thrift,如Hadoop,Cassandra等。

附:thrift与protocal buffer比较

从上面的比较可以看出,thrift胜在“丰富的特性“上,而protocal buffer胜在“文档化”非常好上。在具体实现上,它们非常类似,都是使用唯一整数标记字段域,这就使得增加和删除字段与不会破坏已有的代码。

它们的最大区别是thrift支持完整的client/server RPC框架,而protocal buffer只会产生接口,具体实现,还需要用户做大量工作。

另外,从序列化性能上比较,Protocal Buffer要远远优于thrift,具体可参考:http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/?ca=drs-tp4608 。

8. 参考资料

(1) http://stuartsierra.com/2008/07/10/thrift-vs-protocol-buffers

(2) Thrift: Scalable Cross-Language Services Implementation. Mark Slee, Aditya Agarwal and Marc Kwiatkowski. Facebook

(3) Thrift网站:http://thrift.apache.org/

(4) Protocal Buffer网站:

http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/overview.html

原创文章,转载请注明: 转载自董的博客

使用Thrift RPC编写程序相关推荐

  1. LinuxC/C++编程基础(24) 使用thrift/rpc开发简单实例(续2)

    写在前面:前面两篇文字已经把thrift/rpc的安装以及服务端的编写叙述了,这里再把客户端的编写加上 一.client.cpp文件实现,如下: #include "../gen-cpp/M ...

  2. java十个整数相反顺序_编写程序,对输入的一个整数,按相反顺序输出该数。例如,输入为 3578, 输出为 8753。...

    编写程序,对输入的一个整数,按相反顺序输出该数.例如,输入为 3578, 输出为 8753. package HomeWork08; import java.util.Scanner; public ...

  3. 实验2  使用T-SQL编写程序

    实验2  使用T-SQL编写程序 [实验目的] 1)掌握常用函数的使用方法. 2)掌握流程控制语句的使用方法. [实验环境] SQL Server 2012 Express(或SQL Server 2 ...

  4. 编写程序记录文件位置

    当我们编写程序是会注意到,首先是配置一些函数的结构体. 所以我们就要找到下面的界面,然后打开FWLB中.c文件下面所对应的.h文件,这样就能查找到相应的结构体.下图为我所找到的中断的结构体. 然后就是 ...

  5. 将一张100元的钞票换成1元、5元和10元的钞票,每种钞票至少一张,编写程序输出所有的换法,循环次数需要尽可能地少。

    2.将一张100元的钞票换成1元.5元和10元的钞票,每种钞票至少一张,编写程序输出所有的换法,循环次数需要尽可能地少. public class Two_2money { public static ...

  6. 电大计算机dm编写程序,中央电大计算机组成原理与汇编语言试题.docx

    中央电大计算机组成原理与汇编语言试卷 一.填空题(每空1分) 1>冯?诺依曼体制中最核心的思想是计算机采用 存储程序工作方式. 2.有一机器字 24位,其中操作码占 4位.若采用二地址寻址,则每 ...

  7. 编写程序创建一个通讯录文件,在其中存入10位同学的姓名、年龄、电话号码,并在屏幕上输出第2、4、6、8、10位同学的信息

    <程序设计基础-c语言>杨莉 刘鸿翔 ISBN-978-7-03-032903-5 p257 习题8 8.编写程序创建一个通讯录文件,在其中存入10位同学的姓名.年龄.电话号码,并在屏幕上 ...

  8. 编写程序,在文件file1.dat中存入字符串“good morning”,然后将file1.dat中的内容输出到屏幕上,并复制到文件file2.dat中

    <程序设计基础-c语言>杨莉 刘鸿翔 ISBN-978-7-03-032903-5 p257 习题8 7.编写程序,在文件file1.dat中存入字符串"good morning ...

  9. 编写程序,输入某年某月,求该月有多少天

    <程序设计基础-c语言>杨莉 刘鸿翔 ISBN-978-7-03-032903-5 p91 习题3 9.编写程序,输入某年某月,求该月有多少天. #include<stdio.h&g ...

最新文章

  1. c/c++左值和右值
  2. python3 基本书写规范
  3. IT 巡检内容、方法大全
  4. 窗口之间值、控件的传递
  5. 我的世界服务器破坏方块有延迟,问一下我的世界游戏方块不会被破坏的指令以及指令适用于游戏的版本 我记得在服务器上和单机上好像有不同的指令...
  6. ibm服务器和微软,微软与IBM不得不说的事情
  7. 带有帐号密码验证的apche服务器文件下载
  8. python中如何定义函数的传入参数是option的_python – 当使用@ click.option将命令行参数传递给函数时,如何返回值?...
  9. Current HIVE_LIB is not valid, please export HIVE_LIB='YOUR_LOCAL_HIVE_LIB'
  10. 极兔68亿收购百世快递
  11. 大专适合学习php么_中专毕业上大专好还是出来工作?
  12. ssm-学子商城-项目第十三天最后一天完成项目
  13. Pycharm中如何pip下载包更快
  14. php wps 读取word内容,关于PHP导出WORD带图片
  15. 基于STM32F0实现人体红外传感器
  16. 手机总是自动重启怎么处理
  17. 解决ModuleNotFoundError: No module named ‘celery.five‘的问题
  18. java:数组的静态和动态声明
  19. 微信气泡主题设置_米老鼠微信主题怎么设置? 米老鼠微信主题气泡设置教程来啦!...
  20. 计算机视觉(CV)机器学习(ML)顶会CVPR ICCV ECCV ICML ICLR NeurIPS文章学习链接汇总

热门文章

  1. Check failed: weights_.Size() == num_row_ (38997 vs. 383852) : Size of weights must equal to number.
  2. 2020年第十一届蓝桥杯 - 省赛 - C/C++大学生A组 - C.蛇形填数
  3. 《算法竞赛入门经典》习题3-1 得分(Score,ACM、ICPC Seoul 2005,UVa1585)
  4. 【Qt】2D绘图之涂鸦板
  5. 【Tools】VNC Viewer 6.20安装详解
  6. 【STM32】FreeRTOS列表应用示例
  7. 【STM32】FreeRTOS临界区
  8. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
  9. python有什么用处案例_为什么大家都推荐你学python?看完这5个例子就明白了!...
  10. python3遍历选中文件夹下的文件【GUI编程】