C++Thrift使用手册———一文教会你快速上手Thrift

一、什么是Thrift框架

1.1 RPC框架

  RPC(Remote Procedure Call,远程过程调用),是使程序A调用程序B中方法的手段,常用于分布式系统间的通信,是C/S(客户端/服务端)架构的。比如图中客户端A想调用服务端B中的方法Add(int a, int b),客户端A先将对象序列化为二进制传输到服务端B中,在服务端B需要做反序列化将收到的二进制转换为对象。同时为确保服务稳定,常采用TCP/IP协议。
在这里插入图片描述

1.2Thrift框架

   Thrift框架是一个轻量级、跨语言、易部署的RPC框架。于2007年由FaceBook开发,2008年进入Apache开源项目。通过自身的IDL语言生成客户端和服务端的模板代码,使用者仅需要编写IDL语言则可使用。Thrift主要分为四个层级:传输层(Transport Layer)、协议层(Protocol Layer)、处理层(Processor Layer)、服务层(Server Layer)。

层级内容
传输层负责从网络中读取和写入数据,定义具体的网络传输协议,如TCP/IP
协议层定义数据传输格式,负责序列化和反序列化,如JSON、XML、二进制数据
处理层由IDL(接口描述语言)生成,自动封装了底层网络传输和序列化方式,委托给用户实现的Handler处理
服务层整合传输层、协议层、处理层,提供具体网络IO模型(单线程/多线程/事件驱动),形成最终服务

1.3Thrift框架特点

Thrift具有以下特点,在我们使用的时候可以做一个选择:
(1)开发速度快
   编写RPC接口Thrift IDL文件则可通过编译生成器自动生成服务端骨架(Skeletons)和客户端桩(Stubs)。省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等工作。对于服务端来说只需要按服务骨架(接口),编写具体业务处理程序(Handler)实现类即可。而对于客户端,只要拷贝好IDL定义好的客户端桩和服务对象,就可以像调用本地方法一样进行远程调用。
(2)接口维护简单
   仅需维护Thrift格式的IDL(接口描述语言)则可作为客户端使用的接口文档,同时会自动根据IDL生成相对应的接口代码。
(3)学习成本低
   由Google Protobuf开发团队开发,IDL文件类似Google Protobuf。
(4)跨语言支持
   支持多种语言,包括C++、Python、Java等热门语言,同时客户端和服务端所使用的语言可以不一致。
(5)稳定
   有很多大型企业,如国外的Facebook,国内的百度、美团、小米等都在使用。

二、Thrift框架的简易部署

2.1下载Thrift源码

通过Thrift的官方开源地址可以获取到源码:Thrift源码Github地址
下载之后进入complier里找到cpp工程进行编译得到可执行文件exe,不过这种方式太麻烦,有以下要求:

  • VS C++
  • Flex and Bison, 即 WinFlexBison 包
  • Apache Thrift要求
    (内容大致是一些C++、boost等,以及每种语言对应要求的库。C++要求的是boost、libevent)
    在搜索过程中还找到两种很棒的安装方法,Windows端通过Vcpkg进行安装,Linux可以通过GNU做辅助安装。
    vcpgk和GNU配置环境这里贴上前辈的链接,使用vspkg之后很方便,附上vcpkg的中文使用链接vcpkg中文使用手册,该工具类似Python的pip,使用起来非常顺手。

三、Thrift框架的使用

3.1 支持数据类型

在编写Thrift文件前,先让我们熟悉下这个框架较常见的数据类型,Thrift主要支持三部分的数据类型,分别是基本类型、特殊类型、结构体。

3.1.1 基本类型

Thrift框架支持的基本数据类型主要有(括号为Java对应类型):

类型描述
bool(boolean)布尔类型(TRUE or FALSE)
byte(byte)8位带符号整数
i16(short)16位带符号整数
i32(int)32位带符号整数
i64(long)64位带符号整数
double(double)64位浮点数
string(String)采用UTF-8编码的字符串

3.1.2 特殊类型

类型描述
binary(ByteBuffer)未经过编码的字节流

3.1.3 结构

Thrift支持的结构体和C、C++里的结构体大致相同,不同点在于声明的时候写法不一致,定义了一个OOP(面向对象),但是它不具有继承性。
比如定义一个需要传输和使用的结构体students:

struct students
{
	1:i16 age,
	2:string name
}

3.1.4 容器

同时在Thrift中还支持List、Map、Set容器。

类型描述
list<T>装有T类型的列表
map<T,T>装有T类型的表
set<T>装有T类型的唯一数组

3.2 Thrift 脚本编写

编写基本的Thrift脚本可以阅读官网wikiThrift官网wiki
在使用前,要明确需要的数据。

首先新建一个文件,例如我下面新建的DeviceToUi.thrift。

struct MonitorTemps
{
	1:list<double> Temps
}

service DeviceToUi
{
	void ReportDeviceTemps(1:MonitorTemps monitortemps);
}

thrift支持很多不同的数据类型,比如上面定义了一个结构体MonitorTemps,包含容器list Temps。然后service定义的是你需要在服务端调用的函数ReportDeviceTemps()。下面我们就可以生成对应的资源文件(.cpp)和头文件(.h)。
虽然定义的是List类型,但是在代码里赋值给它需要是vector的,这里还不明白。

3.3 生成头文件和资源文件

如果是用vcpkg操作,在vcpkg的pakeage里有tool文件夹,进入可以看到thrift.exe可执行工具。
在当前文件夹名字的地方输入cmd命令,执行下面这条cmd语句。

thrift.exe -r -gen cpp DeviceToUi.thrift

之后看到当前目录下产生了文件。

3.4 新建解决方案

在新建的解决方案里建立两个项目,分别是client和sercvice,其余文件都已经给我们生成好了,我们自己需要写的就是作为服务端入口函数的sklern资源文件和client端的资源文件。

注意:只有client的代码需要我们写,server的代码可以根据自己情况来考虑。

client.cpp的代码如下:

#include "DeviceToUi.h"  //客户端项目这里引入上面thrift文件定义的接口
#include<thrift/transport/TSocket.h>
#include<thrift/transport/TBufferTransports.h>
#include<thrift/protocol/TBinaryProtocol.h>
#include<vector>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

int main()
{
    MonitorTemps sTemps; //定义传输的结构体,前面为Thrift中定义的
    ::std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", 9090)); //客户端连接当前端口
    ::std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    ::std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    transport->open();
    std::vector<double> testData(10,5);
    sTemps.Temps = testData;
    DeviceToUiClient client(protocol); //调用自动生成的client对象,通过client对象调用服务端方法
    printf("客户端开始调用服务端的方法:ServiceFunc()\n传输十个数据为5的数组数据。");
    client.ReportDeviceTemps(sTemps); //到这一步客户端已经往服务端发送了数据,服务端的方法,传输结构体
    system("pause");
}

server.cpp的代码如下:

// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.

#include "DeviceToUi.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <algorithm>
#include <iostream>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;

class DeviceToUiHandler : virtual public DeviceToUiIf {
 public:
  DeviceToUiHandler() {
    // Your initialization goes here
  }

  void ReportDeviceTemps(const MonitorTemps& monitortemps) {
    // Your implementation goes here
    printf("ReportDeviceTemps\n");
    printf("服务端接收到数据,收到的数据是:\n");
    std::for_each(monitortemps.Temps.begin(), monitortemps.Temps.end(), [](const auto& i) {std::cout << i << " "; });
  }

};

int main(int argc, char **argv) {
  int port = 9090;
  ::std::shared_ptr<DeviceToUiHandler> handler(new DeviceToUiHandler());
  ::std::shared_ptr<TProcessor> processor(new DeviceToUiProcessor(handler));
  ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

  TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  server.serve();
  return 0;
}


3.5 建立Client项目和Server项目

调试开启两个项目,可以看到之间进行通信了。

启动多个项目,在VS的解决方案中右键属性。
在这里插入图片描述
可以看到我们的列表数据已经传送过去。

在这里插入图片描述
此时两个项目的文件结构是:
在这里插入图片描述
大部分是自动生成的代码,使用起来很方便。

3.6 命名空间

Thrift内部添加命名空间,采用 文件名+结构体,比如同等目录下有test.thrift,里面有结构体hello。则调用的时候为test.hello作为接口变量。

若含在cpp里面,则添加namespace cpp test.hello,代码里可以通过test::hello::xxxx来使用,xxx结构体里的内容。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

面条有点辣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值