Thrift 在Windows环境下的编译与简单C++应用

前言

Thrift是Facebook提供的一个跨语言的服务部署框架,可以实现客户端和服务器远程过程调用。相较于Google的grpc框架,Thrift对三方库依赖更少,编译更简单,并且运行效率也更高。Thrift只依赖boost、openssl和libevent三个库,本次测试编译的都是静态库。下面详细介绍thrift 0.15.0的编译过程。

一、编译thrift

1.1 编译前准备

编译环境:win11+vs2019

编译前下载源码库

boost_1_78_0.tar.gz boost下载地址

openssl-1.1.1.tar.gz openssl下载地址

libevent-2.1.10-stable.tar.gz libevent下载地址

thrift-0.15.0.tar.gz thrift-0.15.0下载地址 源代码包

thrift-0.15.0.exe thrift-0.15.0.exe 用于在Windows下生成目标语言的桩代码

网上教程太多太多,可能别人博主能成功,你自己不能编译成功,所以还是的自己实实在在的编译一遍。我主要参考如下几个才编译出来Thrift框架-Windows-C++ x64编译thrift在windows上编译

但是现象也不是和上述博主一样,所以将这次编译过程记录一下,以供参考。

1.2 thrift源码编译

进入thrift-0.15.0\lib\cpp,打开thrift.sln解决方案,有libthrift,libthriftnb两个工程。两个工程的区别是,libthriftnb工程是非阻塞(non-blocking)模式的服务器,非阻塞模式需要依赖libevent库。

1.2.1 libthrift工程配置

 修改代码:

(1)修改thrift-0.15.0\lib\cpp\src\thrift\thrift_export.h,添加#define thrift_EXPORTS

打开<thrift目录>\lib\cpp\src\thrift\thrift_export.h,在文件中添加#define thrift_EXPORTS。

由于我们需要编译thrift生成静态链接库,因此需要添加定义:#define thrift_EXPORTS,将THRIFT_EXPORT设置为__declspec(dllexport)。

(2)如果出现下面的错误,则将报错的地方的#include <thrift/config.h>

则修改为#include <thrift/thrift-config.h>

(3)如果concurrency筛选器下面的部分源文件名称与源码目录下的文件名称不一致,需要移除,我的已经移除了。

1.2.2 libthriftnb工程配置

二、C++应用开发

  1. 写自己的接口描述文件(Interface Description File)
  2. 用thrift-0.15.0.exe生成目标语言(C++)代码
  3. 服务端程序引入生成的代码,实现RPC业务代码
  4. 客户端程序引入生成的代码,调用远程服务接口

2.1 thrift IDL文件

thrift IDL不支持无符号的数据类型,因为很多编程语言中不存在无符号类型,thrift支持一下几种基本的数据类型

  • byte: 有符号字节
  • i16: 16位有符号整数
  • i32: 32位有符号整数
  • i64: 63位有符号整数
  • double: 64位浮点数
  • string: 字符串

此外thrift还支持以下容器类型:

  • list: 一系列由T类型的数据组成的有序列表,元素可以重复;
  • set: 一系列由T类型的数据组成的无序集合,元素不可重复;
  • map: 一个字典结构,Key为K类型,Value为V类型,相当于c++中;

thrift容器中元素的类型可以是除了service之外的任何类型,包括exception。thrift也支持文件包含,相当于CPP中的include。

#///**/都可以作为thrift文件中的注释。

thrift提供两个关键字requiredoptional,分别用于表示对应的字段是必填的还是可选的(推荐尽量使用optional)

这里给出一个thrift的IDL基本语法列表,详细用法可以去官网查找

namespace cpp thrift.Test
//typedef 用法
typedef i32 MyInt32;
typedef string MyString;
typedef i32 UserId;
//struct 结构定义
struct TypedefTestStruct
{
	1: MyInt32 field_MyInt32;
	2: MyString field_MyString;
	3: i32 field_Int32;
	4: string filed_string;
}
//enum 枚举定义
enum Numberz
{
	ONE = 1,
	TWO,
	THREE,
	FIVE = 5,
	SIX,
	EIGHT = 8
}
//const 用法
const Numberz myNumberz = Numberz.ONE;
struct Bonk
{
	1: string message,
	2: i32 type
}
//类型嵌套
struct Xtruct
{
	1: string string_thing,
	2: i8 byte_thing,
	3: i32 i32_thing,
	4: i64 i64_thing
}
struct Xtruct2
{
	1: i8 byte_thing,
	2: Xtruct struct_thing,
	3: i32 i32_thing
}
//支持map list set类型分别对应C++中的 map = stl::map list = stl::vector set = stl::set
typedef map<string, Bonk> MapType
struct Insanity
{
	1: map<Numberz, UserId> userMap;
	2: list<Xtruct> xtructs;
}
struct CrazyNesting
{
	1: string string_field,
	2: optional set<Insanity> set_field;
	3: required list<map<set<i32>, map<i32,set<list<map<Insanity,string>>>>>> list_field,
	4: binary binary_field
}
//union用法
union SomeUnion
{
	1: map<NumberZ, UserId> map_thing,
	2: string string_thing,
	3: i32 i32_thing,
	4: Xtruct3 xtruct_thing,
	5: Insanity insanity_thing
}
//exception 异常
exception Xception
{
	1: i32 errorCode,
	2: string message
}
exception Xception2
{	
	1: i32 errorCode,
	2: Xtruct struct_thing
}
// empty struct
struct EmptyStruct{}
struct OneField
{
	1: EmptyStruct field;
}
//service 定义的一组rpc服务,一般是抽象出来的接口调用
service ThriftTest
{
	void testVoid(),
	string testString(1: string thing),
	bool testBool(1: bool thing),
	i8 testByte(1: i8 thing),
	i32 testI32(1: i32 thing),
	i64 testI64(1: i64 thing),
	Xtruct testStruct(1: Xtruct thing),
	Xtruct2 testNest(1: Xtruct2 thing),
	map<string, string> testStringMap(1: map<string, string> thing),
	set<i32> testSet(1: set<i32> thing),
	list<i32> testList(1: list<i32> thing),
	Numberz testEnum(1: Numberz thing),
	map<i32, map<i32,i32>> testMapMap(1: i32 hello),
	map<UserId, map<Numberz,Insanity>> testInsanity(1: Insanity argument),
	Xtruct testMulti(1: i8 arg0, 2: i32 arg1, 3: i64 arg2, 4: map<i16, string> arg3, 5: Numberz arg4, 6: UserId arg5),
	void testException(1: string arg) throws(1: Xception err1),
	Xtruct testMultiException(1: string arg0, 2: string arg1) throws(1: Xception err1, 2: Xception2 err2),
	oneway void testOneway(1:i32 secondsToSleep)
}

对于返回void的函数,thrift仍然会确保函数返回,这样表示这个函数被正确执行,且服务端已有返回信息了。但是如果给void的函数前加上oneway,此函数以异步模式执行,这样在调用此函数后,函数会立即返回,那么此函数的返回只能表示数据已经进入传输层,并不能表示服务器端已经接收到并返回了数据。 

所以oneway不安全,但是效率高一些,在不要求一定要发送成功的情况下(可靠性要求不高)可以使用。

2.2  定义IDL文件并生成目标代码

namespace cpp thrift.Test
service ThriftTest
{
	i32 add(1: i32 arg1,2: i32 arg2),
	void showName(),
}

 thrift-0.15.0.exe -r --gen cpp test.thrift生成对应的c++代码,成功后生成一个gen-cpp文件夹,将代码引入服务端和客户端。

2.3 服务端配置和客户端配置

新建client.cpp源代码:

#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include "ThriftTest.h"

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

//链接库文件 
#pragma comment(lib,"libthrift.lib")

int main(int argc, char** argv)
{
	int port = 9090; 
	std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", port));
	std::shared_ptr<TBufferedTransport> transport(new TBufferedTransport(socket));
	std::shared_ptr<TBinaryProtocol> protocol(new TBinaryProtocol(transport)); 
	ThriftTestClient client(protocol);
	try 
	{
		transport->open(); 
		client.showName();
		auto result = client.add(1, 2);
		printf("result = %d", result);
		transport->close();
	}
	catch (TException& tx)
	{ 
		printf("ERROR:%s\n", tx.what()); 
	} 
	return 0;
}

服务端ThriftTest_server.skeleton.cpp代码

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

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

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

using namespace  ::thrift::Test;
//链接库文件 
#pragma comment(lib,"libthrift.lib")

class ThriftTestHandler : virtual public ThriftTestIf {
 public:
  ThriftTestHandler() {
    // Your initialization goes here
  }

  int32_t add(const int32_t arg1, const int32_t arg2) {
    // Your implementation goes here
    printf("客户端调用add\n");
    return arg1 + arg2;
  }

  void showName() {
    // Your implementation goes here
    printf("客户端调用showName\n");
  }

};

int main(int argc, char **argv) {
  int port = 9090;
  ::std::shared_ptr<ThriftTestHandler> handler(new ThriftTestHandler());
  ::std::shared_ptr<TProcessor> processor(new ThriftTestProcessor(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;
}

 

 客户端可以像调用本地的方法一样调用服务端的方法,这就是RPC调用。

  • 28
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值