thrift是一个Apache公司开源的一款RPC(Remote Procedure Call)框架,让不同语言构建的服务可以做到远程调用无缝对接。
thrift库分两部分:
libthrift - 核心库文件,需要依赖OpenSSL、boost
libthriftnb - 包含thrift非阻塞服务器, 需要libevent
编译环境
操作系统:windows 10
Qt:5.12.10 MinGW64
这里因为项目需要,编译MinGW64版本的thrift。Qt安装完成后将下列路径添加到环境变量:
D:\Qt\Qt5.12.10\Tools\mingw730_64\bin
D:\Qt\Qt5.12.10\5.12.10\mingw73_64\bin
一.MingW64编译boost
boost1.81.0
1.生成编译工具
加压boost源码,cmd中输入
bootstrap.bat gcc
表示使用“gcc”工具集(即我们安装的64位MingW)生成编译工具“b2.exe”
2.编译并安装boost
b2.exe install --build-type=complete threading=multi link=static address-model=64 toolset=gcc
stage表示只生成库(dll和lib),install还会生成包含头文件的include目录;
--build-type=complete表示同时生成Debug和Release版本;
threading=multi表示线程方式使用“multi ”(多线程);
link=static表示编译成静态库,shared表示编译成动态库;
address-model=64表示地址模型使用64位(即生成的库均为64位库);
toolset=gcc表示工具集使用“gcc”
可以使用b2.exe --help查看所有参数,可通过--prefix="XXXXXX"指定安装位置,如果不指定位置,默认安装到C:\Boost
二.MingW64编译OpenSSL
OpenSSL1.1.1q
下载安装msys2,我选的是最新的msys2-x86_64-20221216.exe,默认安装到C:\msys64
msys2是一个模拟的Linux命令行开发环境。msys2里本身就带了mingw,如果要用自己开发环境的mingw(也就是Qt自带的mingw,前面已添加到windows环境变量)。这里还需要在windows系统变量中创建一个变量MSYS2_PATH_TYPE,值为inherit,msys2就会继承windows系统的环境变量,去找寻windows中已经配置好的mingw工具。重新打开MSYS2 MSYS,输入gcc,没有提示找不到文件就说明刚设置的变量生效了。
如果要使用msys2自带的mingw,需要打开MSYS2 MINGW32或MSYS2 MINGW64。
经测试,这两种方式编译的OpenSSL都能正常使用。
.pro文件中添加
INCLUDEPATH += C:\msys64\home\openssl-1.1.1q\mingw64\include
LIBS += C:\msys64\home\openssl-1.1.1q\mingw64\lib\libcrypto.a \
C:\msys64\home\openssl-1.1.1q\mingw64\lib\libssl.a
LIBS += -lWs2_32 -lCrypt32
注意要添加Ws2_32和Crypt32这两个windows库,否则很多函数找不到
下载OpenSSL,解压到C:\msys64\home\
1.配置
在openssl-1.1.1q新建mingw64文件夹,用来存放安装的文件
打开MSYS2 MINGW64,输入
cd C:/msys64/home/openssl-1.1.1q
./configure mingw64 no-shared --prefix="C:/msys64/home/openssl-1.1.1q/mingw64"
默认编译的是动态库,这里指定no-shared生成静态库
我这正常。这一步如果提示缺少perl,“/usr/bin/perl:bad interpreter:No such file or directory”,可以用指令pacman -S perl安装一个。
2.编译
make
如果提示“-bash: make: command not found”,可以用指令pacman -S make安装一个
3.安装
make install
刚进行到这一步,突然发现boost和OpenSSL可以直接安装.......,上面的内容请忽略
分割线***************************************************************************
这里是官方用MSYS2编译thrift的参考链接:https://github.com/apache/thrift/blob/master/build/cmake/README-MSYS2.md
明确指出通过这种编译方式无法生成非阻塞的服务器类型,因为libevent无法正常工作。
Note: libevent and libevent-devel do not work with this toolchain as they do not properly detect mingw64 and expect some headers to exist that do not, so the non-blocking server is not currently built into this solution.
因此,thrift传输层中的TNonblockingTransport和服务端类型中的TNonblockingServer都无法使用
thrift的协议(应用层)
thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:
TBinaryProtocol:二进制编码格式进行数据传输
TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输 (常用的)
TJSONProtocol: 使用JSON文本的数据编码协议进行数据传输
TSimpleJSONProtocol:只提供JSON只写的协议,适用于通过脚本语言解析
thrift的传输层
TSocket:使用阻塞式I/O进行传输,是最常见的模式
TNonblockingTransport:使用非阻塞方式,用于构建异步客户端
TFramedTransport:使用非阻塞方式,按块的大小进行传输,类似于Java中的NIO
thrift的服务端类型
TSimpleServer:单线程服务器端,使用标准的阻塞式I/O
TThreadPoolServer:多线程服务器端,使用标准的阻塞式I/O
TNonblockingServer:单线程服务器端,使用非阻塞式I/O
THsHaServer:半同步半异步服务器端,基于非阻塞式IO读写和多线程工作任务处理
TThreadedSelectorServer:多线程选择器服务器端,对THsHaServer在异步IO模型上进行增强
一.安装编译环境
打开MSYS2 MINGW64,输入
pacman --needed -S bison flex make mingw-w64-x86_64-openssl \
mingw-w64-x86_64-boost mingw-w64-x86_64-cmake \
mingw-w64-x86_64-toolchain mingw-w64-x86_64-zlib
下载thrift源码,我选择的是最新的thrift-0.17.0,解压到C:\msys64\home
注:thrift-0.17.0.exe也要下载,然后重名为thrift.exe并添加到环境变量,待会需要用它将IDL文件转换成cpp源文件
cd C:/msys64/home/thrift-0.17.0
mkdir ../thrift-build
cd ../thrift-build
mkdir ../thrift-build
cd ../thrift-build
cmake -G"MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=/mingw64/bin/mingw32-make \
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc.exe \
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++.exe \
-DWITH_LIBEVENT=OFF \
-DWITH_SHARED_LIB=OFF -DWITH_STATIC_LIB=ON \
-DWITH_JAVA=OFF -DWITH_PYTHON=OFF -DWITH_PERL=OFF \
../thrift-0.17.0
二.编译
cmake --build .
编译成功,但是会有一个依赖NodeJS的错误,如下图所示。该错误不影响使用,因为所有的test都能测试通过。
三.运行测试
ctest
四.安装
cmake --install .
默认安装到C:\Program Files (x86)\thrift,这一步需要管理员权限,因此得用管理员权限打开MSYS2 MINGW64
五.一个例子
1.编写.thrift文件,生成cpp文件
新建文件student.thrift,输入如下内容
struct Student{
1: i32 sno,
2: string sname,
3: bool ssex,
4: i16 sage,
}
service CaoShangPa{
void put(1: Student s),
}
打开cmd,进入student.thrift文件所在目录,执行如下命令
thrift -r --gen cpp student.thrift
生成如下5个文件
student_types.h
student_types.cpp
CaoShangPa.h
CaoShangPa.cpp
CaoShangPa_server.skeleton.cpp
其中CaoShangPa_server.skeleton.cpp是一个简单的服务端demo
2.客户端
在Qt中新建控制台工程Thrift_Client,将上面的除CaoShangPa_server.skeleton.cpp外的四个文件添加到工程中,main.cpp修改为如下所示
#include "CaoShangPa.h"
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <iostream>
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
//using boost::shared_ptr;
int main(int argc, char **argv) {
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();
Student s;
s.sno = 123;
s.sname = "gavin";
s.ssex = 1;
s.sage = 30;
CaoShangPaClient client(protocol);
std::cout<<"Client send a data"<<std::endl;
std::cout<<"no:123"<<std::endl;
std::cout<<"name:gavin"<<std::endl;
std::cout<<"sex:1"<<std::endl;
std::cout<<"age:30"<<std::endl;
client.put(s);
transport->close();
return 0;
}
3.服务端
在Qt中新建控制台工程Thrift_Server,将上面的除CaoShangPa_server.skeleton.cpp外的四个文件添加到工程中,main.cpp修改为如下所示
#include "CaoShangPa.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <iostream>
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
class CaoShangPaHandler : virtual public CaoShangPaIf {
public:
CaoShangPaHandler() {
// Your initialization goes here
}
void put(const Student& s) {
// Your implementation goes here
std::cout<<"Server receive a data"<<std::endl;
std::cout<<"no:"<<s.sno<<std::endl;
std::cout<<"name:"<<s.sname<<std::endl;
std::cout<<"sex:"<<s.ssex<<std::endl;
std::cout<<"age:"<<s.sage<<std::endl;
}
};
int main(int argc, char **argv) {
std::cout<<"Server started!"<<std::endl;
int port = 9090;
::std::shared_ptr<CaoShangPaHandler> handler(new CaoShangPaHandler());
::std::shared_ptr<TProcessor> processor(new CaoShangPaProcessor(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;
}
4.pro文件配置
Thrift_Client.pro和Thrift_Server.pro中都要添加如下内容
INCLUDEPATH += "C:\Program Files (x86)\thrift\include" #路径中有空格要加引号
INCLUDEPATH += C:\Boost\include\boost-1_81
LIBS += -L"C:\Program Files (x86)\thrift\lib" -lthrift
LIBS += -lWs2_32
我这编译的时候会报错
C:\Program Files (x86)\thrift\include\thrift\windows\config.h:79: error: afunix.h: No such file or directory
解决方法
打开config.h,将#ifdef HAVE_AF_UNIX_H改成#ifndef HAVE_AF_UNIX_H
5.运行
先运行Thrift_Server,再运行Thrift_Client,结果如下所示
原文链接:https://blog.csdn.net/caoshangpa/article/details/128474421