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++應用開發
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提供兩個關鍵字required和optional,分別用于表示對應的字段是必填的還是可選的(推薦盡量使用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 hereprintf("客戶端調用add\n");return arg1 + arg2;}void showName() {// Your implementation goes hereprintf("客戶端調用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調用。
總結
以上是生活随笔為你收集整理的Thrift 在Windows环境下的编译与简单C++应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab三维点云去除背景,一种点云处
- 下一篇: Python实现截图?一文带你入门