thritf 使用和编译
一 概念
Thrift框架是一个轻量级、跨语言、易部署的RPC框架。于2007年由FaceBook开发,2008年进入Apache开源项目。通过自身的IDL语言生成客户端和服务端的模板代码,使用者仅需要编写IDL语言则可使用。
二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 thfirt 双向通信
备注: 方案来自下面博客
https://zhuanlan.zhihu.com/p/397916864
2.1 方案
轮询
客户端周期性的向服务器请求是否需要进行某种操作,这种方案延迟较大且浪费开销。
双服务器双客户端
就是在服务器和客户端是相互,这种方案需要两个端口两者互为服务器和客户端。
异步共享通道(建立长链接)
Thrift底层实际上是socket,socket天然支持双向传输。我们完全可以通过继承Thrift类来满足我们的需求。
2.1
2.2 异步共享通道(建立长链接)
定义thrift接口
namespace cpp test
namespace py test
service HelloWorldBidirectionService{
oneway void SayHello(1:string msg);
oneway void test(1:string msg);
}
// 备注: 在实现双向thrift时接口的定义一定要是oneway,不然会报端口被强制关闭之类的错误。
服务端
#include <iostream>
#pragma comment(lib, "libthrift_d.lib")
#pragma comment(lib, "libthriftnb_d.lib")
#pragma comment(lib, "libcrypto32MTd.lib")
#pragma comment(lib, "libssl32MTd.lib")
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.
#include "../testCommon/HelloWorldBidirectionService.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/ThreadFactory.h>
#include <thrift/concurrency/Thread.h>
using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::concurrency;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;
using namespace test;
static std::shared_ptr<TTransport> g_CurrentTransport = nullptr;
class HelloWorldBidirectionServiceHandler : virtual public HelloWorldBidirectionServiceIf {
public:
HelloWorldBidirectionServiceHandler() {
// Your initialization goes here
}
void SayHello(const std::string& msg) {
// Your implementation goes here
printf("%s\n", msg.c_str());
std::string msgTmp = msg;
msgTmp += " Server Send";
SayToClient(msgTmp);
}
void test(const std::string& msg) {
printf("test=%s", msg.c_str());
}
void SayToClient(const std::string& msg)
{
shared_ptr<TBinaryProtocol> protocol(new TBinaryProtocol(g_CurrentTransport));
HelloWorldBidirectionServiceClient client(protocol);
//Thread.Sleep(1000);
client.SayHello(msg);
}
};
class HelloWorldBidirectionProcessor : public TProcessorFactory
{
public:
std::shared_ptr<TProcessor> getProcessor(const TConnectionInfo& connInfo)
{
g_CurrentTransport = connInfo.transport;
printf("接收到连接信号\n");
shared_ptr<HelloWorldBidirectionServiceHandler> handler(new HelloWorldBidirectionServiceHandler());
shared_ptr<TProcessor> processor(new HelloWorldBidirectionServiceProcessor(handler));
return processor;
}
};
static std::shared_ptr<TProcessorFactory> getProcessorFactory()
{
shared_ptr<TProcessorFactory> processorFactory(new HelloWorldBidirectionProcessor());
return processorFactory;
}
int main(int argc, char** argv) {
int port = 9090;
shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
shared_ptr<TTransportFactory> transportFactory(new TTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(10);//指定10个线程数
shared_ptr<ThreadFactory> threadFactory = shared_ptr<ThreadFactory>(new ThreadFactory());
threadManager->threadFactory(threadFactory);
threadManager->start();
shared_ptr<TThreadPoolServer> server(new TThreadPoolServer(getProcessorFactory(), serverTransport, transportFactory, protocolFactory, threadManager));
/* shared_ptr<HelloWorldBidirectionServiceHandler> handler(new HelloWorldBidirectionServiceHandler());
shared_ptr<TProcessor> processor(new HelloWorldBidirectionServiceProcessor(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;
}
客户端代码
#include <iostream>
#pragma comment(lib, "libthrift_d.lib")
#pragma comment(lib, "libthriftnb_d.lib")
#pragma comment(lib, "libcrypto32MTd.lib")
#pragma comment(lib, "libssl32MTd.lib")
#include <iostream>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>
#include "../testCommon/HelloWorldBidirectionService.h"
#include <thread>
using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::concurrency;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace test;
class HelloWorldBidirectionFace :public HelloWorldBidirectionServiceIf
{
public:
void SayHello(const std::string& msg) {
// Your implementation goes here
/*printf("%s\n", msg);
printf("%s\n", &msg);*/
cout << msg << endl;
//cout << &msg << endl;
}
void test(const std::string& msg) {
printf("test=%s", msg.c_str());
}
};
static void Run(std::shared_ptr<TTransport> sock)
{
std::shared_ptr<HelloWorldBidirectionFace> handler(new HelloWorldBidirectionFace());
std::shared_ptr<TProcessor> processor(new HelloWorldBidirectionServiceProcessor(handler));
//boost::shared_ptr<HelloWorldBidirectionServiceProcessor> processor(new HelloWorldBidirectionFace());
try
{
std::shared_ptr<TProtocol> inProtocol(new TBinaryProtocol(sock));
std::shared_ptr<TProtocol> outProtocol(new TBinaryProtocol(sock));
while (processor->process(inProtocol, outProtocol, (void*)"proc"))
{
printf("wait next msg\n");
}
}
catch (TException& tx)
{
printf("connect close\n");
cout << "ERROR1: " << tx.what() << endl;
}
}
static void RecFromConsole(std::shared_ptr<HelloWorldBidirectionServiceClient> client)
{
string str;
cout << "input exit stop" << endl;
cin >> str;
while (str.compare("exit"))
{
client->SayHello(str);
client->test("1122");
cin >> str;
}
}
int main()
{
std::shared_ptr<TTransport> socket(new TSocket("localhost", 9090));
std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
std::shared_ptr<HelloWorldBidirectionServiceClient> client(new HelloWorldBidirectionServiceClient(protocol));
try {
transport->open();
std::thread mythread(Run, socket);
std::thread mythread2(RecFromConsole, client);
mythread2.join();
transport->close();
}
catch (TException& tx) {
cout << "ERROR: " << tx.what() << endl;
}
}
四 thrift 编译
因thrift依赖boost、libevent、openssl、zlib,因此需要先编译这些库
1 windows VS2015编译
(1) vs2015编译boost
- 解压boost_1_75_0.7z到D:\soft,解压后路径为D:\soft\boost_1_75_0
- 在soft目录下创建boost_build文件夹;
- 开始菜单->visual studio 2015->VS2015 x86 x64 兼容工具命令提示符;
注:编译64位的动态库使用该命令提示符工具,编译其他版本选择对应的命令提示符工具 - 在命令提示符工具中输入如下命令:
cd D:\soft\boost_1_75_0
bootstrap.bat
b2 stage --toolset=msvc-14.0 architecture=x86 address-model=64 --without-graph --without-graph_parallel --stagedir=“D:\soft\boost_build” link=static runtime-link=shared runtime-link=static threading=multi debug release - 编译需要10-30分钟,编译完成后,最终的boost库在D:\soft\boost_build下面;
- 将D:\soft\boost\include\boost-1_75\boost目录拷贝到D:\Program Files\boost_1_75_0\vs2015\x64\include目录下;
- 将D:\soft\boost_build\lib目录下的所有文件拷贝到D:\Program Files\boost_1_75_0\vs2015\x64\lib目录下;
(2) vs2015编译libevent
- 解压libevent-2.1.12-stable.tar.gz到D:\soft,解压后路径为D:\soft\libevent-2.1.12-stable;
- 记事本打开D:\soft\libevent-2.1.12-stable\minheap-internal.h,在文件头添加#include<stdint.h>
注:如果不加该头文件,会报UINT32_MAX”未声明的标识符的错误 - 开始菜单->visual studio 2015->VS2015 x86 x64 兼容工具命令提示符;
注:编译64位的动态库使用该命令提示符工具,编译其他版本选择对应的命令提示符工具 - 在命令提示符工具中输入如下命令:
cd D:\soft\libevent-2.1.12-stable;
nmake /f Makefile.nmake static_libs
注:全量编译:nmake /f Makefile.nmake [all] //all可以省略,默认是all
库编译: nmake /f Makefile.nmake static_libs
测试编译:nmake /f Makefile.nmake tests
清理输出:nmake /f Makefile.nmake clean
编译完成,在D:\soft\libevent-2.1.12-stable目录下会生成 libevent.lib、libevent_core.lib和libevent_extras.lib动态库; - 将生成的3个动态库拷贝到D:\Program Files\libevent-2.1.12-stable\vs2015\x64\lib目录下;
- 将D:\soft\libevent-2.1.12-stable\include目录拷贝到D:\Program Files\libevent-2.1.12-stable\vs2015\x64\include;
- 将D:\soft\libevent-2.1.12-stable\WIN32-Code\nmake\event2\event-config.h文件拷贝到D:\Program Files\libevent-2.1.12-stable\vs2015\x64\include\event2目录下;
(3) openssl 编译
openssl使用的版本是 openssl-1.1.1 可以下载也可以自己编译
(4) thrift 编译
- 将D:\soft\thrift-0.14.1\config.h文件拷贝到D:\soft\thrift-0.14.1\lib\cpp\src\thrift目录下;不然在编译的时候会报找不到<thrift/config.h>文件;
- 修改拷贝过去的config.h文件,将所有HAVE开头的宏都注释掉;
- 使用vs2015打开D:\soft\thrift-0.14.1\lib\cpp\thrift.sln解决方案文件;
- 打开后可以看到libthrift和libthriftnb两个工程,这里我们编译libthrift即可
注:libthriftnb工程是非阻塞(non-blocking)模式的服务器,非阻塞模式需要依赖libevent库;(实际编译情况来看libthrift也依赖libevent库) - 增加依赖的库和头文件
- 编译
2 国产编译
(1)编译libevent
// 编译mac
./configure --prefix=/Users/admin/xhw/thrift/3rd/libevent/mac
// 编译x86——64
./configure --disable-openssl --prefix=/root/xhw/thrift/compile/3rd/libevent/x86_64
// 交叉编译aarch64 libevent库
./configure --prefix=/root/xhw/thrift/compile/3rd/libevent/aarch64 --host=arm-linux CC=/root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-gcc CXX=/root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-g++
// 交叉编译loongarch64 libevent库
./configure --prefix=/root/xhw/thrift/compile/3rd/libevent/loongarch --host=arm-linux CC=/root/cross-compile/node/loongarch64/node_build_loongarch64/bin/loongarch64-linux-gnu-gcc CXX=/root/cross-compile/node/loongarch64/node_build_loongarch64/bin/loongarch64-linux-gnu-g++
(2) 编译boost库
// x86_64
./bootstrap.sh --with-libraries=all --with-toolset=gcc
./b2 install --prefix=/root/xhw/thrift/compile/3rd/boost/x86_64
// mac
./bootstrap.sh --with-libraries=all --with-toolset=gcc
./b2 install --prefix=/Users/admin/xhw/thrift/3rd/boost/mac
// 交叉编译 aarch64
./bootstrap.sh --with-libraries=all --with-toolset=gcc
//修改 配置文件project-config.jam
if ! gcc in [ feature.values <toolset> ]
{
using gcc : arm :/root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-gcc
}
./b2
./b2 install --prefix=/root/xhw/thrift/compile/3rd/boost/aarch64
// 交叉编译loongarch
./bootstrap.sh --with-libraries=all --with-toolset=gcc
// 修改 配置文件project-config.jam
if ! gcc in [ feature.values <toolset> ]
{
using gcc : arm :/root/cross-compile/node/loongarch64/node_build_loongarch64/bin/loongarch64-linux-gnu-gcc
}
./b2
./b2 install --prefix=/root/xhw/thrift/compile/3rd/boost/loongarch
(3) 编译thrift库
thriftBuild.sh
#! /bin/bash
# openssl安装目录
export OPENSSL_DIR="/root/xhw/thrift/compile/3rd/openssl/x86_64"
# boost安装目录
export BOOST_ROOT="/root/xhw/thrift/compile/3rd/boost/x86_64"
# libevent安装目录
export LIBEVENT_DIR="/root/xhw/thrift/compile/3rd/libevent/x86_64"
export CC=gcc
export CXX=g++
./configure CXXFLAGS="-fPIC" CFLAGS="-fPIC" --prefix=/root/xhw/thrift/compile/3rd/thrift/x86_64 \
--disable-tests \
--enable-shared \
--disable-tutorial \
--with-cpp \
--without-python \
--without-java \
--without-lua \
--without-perl \
--with-c_glib \
--enable-static \
--with-boost=${BOOST_ROOT} \
--with-libevent=${LIBEVENT_DIR} \
# --with-openssl=${OPENSSL_DIR}
--without-openssl
# --enable-libs=no
thriftBuildAarch64.sh
#! /bin/bash
# openssl安装目录
export OPENSSL_DIR="/root/xhw/thrift/compile/3rd/openssl/aarch64"
# boost安装目录
export BOOST_ROOT="/root/xhw/thrift/compile/3rd/boost/aarch64"
# libevent安装目录
export LIBEVENT_DIR="/root/xhw/thrift/compile/3rd/libevent/aarch64"
export CC=/root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-gcc
export CXX=/root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-g++
export LDFLAGS="-L$OPENSSL_DIR/lib"
./configure CXXFLAGS="-fPIC" CFLAGS="-fPIC" --prefix=/root/xhw/thrift/compile/3rd/thrift/aarch64 --host=x86_64 --target=aarch64 \
--disable-tests \
--disable-shared \
--disable-tutorial \
--with-cpp \
--without-python \
--without-java \
--without-lua \
--without-perl \
--with-c_glib \
--enable-static \
--with-boost=${BOOST_ROOT} \
--with-libevent=${LIBEVENT_DIR} \
--with-ssl=${OPENSSL_DIR}
3 编译错误
(1) 错误一
depbase=`echo src/thrift/TApplicationException.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/bash ../../libtool --tag=CXX --mode=compile /root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-g++ -std=c++11 -DHAVE_CONFIG_H -I. -I../.. -I../../lib/cpp/src/thrift -I../../lib/c_glib/src/thrift -I/root/xhw/thrift/compile/3rd/boost/aarch64/include -I./src -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Wall -Wextra -pedantic -g -O2 -MT src/thrift/TApplicationException.lo -MD -MP -MF $depbase.Tpo -c -o src/thrift/TApplicationException.lo src/thrift/TApplicationException.cpp &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile: /root/cross-compile/sdk/aarch64/usr/bin/aarch64-linux-g++ -std=c++11 -DHAVE_CONFIG_H -I. -I../.. -I../../lib/cpp/src/thrift -I../../lib/c_glib/src/thrift -I/root/xhw/thrift/compile/3rd/boost/aarch64/include -I./src -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Wall -Wextra -pedantic -g -O2 -MT src/thrift/TApplicationException.lo -MD -MP -MF src/thrift/.deps/TApplicationException.Tpo -c src/thrift/TApplicationException.cpp -o src/thrift/TApplicationException.o
In file included from /root/cross-compile/sdk/aarch64/usr/aarch64-buildroot-linux-gnu/include/c++/5.4.0/ext/string_conversions.h:41:0,
from /root/cross-compile/sdk/aarch64/usr/aarch64-buildroot-linux-gnu/include/c++/5.4.0/bits/basic_string.h:5249,
from /root/cross-compile/sdk/aarch64/usr/aarch64-buildroot-linux-gnu/include/c++/5.4.0/string:52,
from ./src/thrift/Thrift.h:37,
from ./src/thrift/TApplicationException.h:23,
from src/thrift/TApplicationException.cpp:20:
/root/cross-compile/sdk/aarch64/usr/aarch64-buildroot-linux-gnu/include/c++/5.4.0/cstdlib:140:11: error: '::malloc' has not been declared
using ::malloc;
^
/root/cross-compile/sdk/aarch64/usr/aarch64-buildroot-linux-gnu/include/c++/5.4.0/cstdlib:153:11: error: '::realloc' has not been declared
解决方式
解决方式: 进入到/root/xhw/thrift/compile/thrift-0.12.0/lib/cpp/src/thrift目录,注释掉config.h目录中的
#define malloc rpl_malloc
#define realloc rpl_realloc
(2) 错误二
解决方式
在 ./config.h /lib/cpp/src/thrift/config.h (如果c_glib也出现 /lib/c_glib/src/thrift/config.h) 中加入宏
#define SIGNED_RIGHT_SHIFT_IS 1
#define ARITHMETIC_RIGHT_SHIFT 1
(3)错误三
src/thrift/TOutput.cpp:110:22: error: invalid conversion from 'char*' to 'int' [-fpermissive]
int rv = strerror_r(errno_copy, b_errbuf, sizeof(b_errbuf));
解决方式
在TOutput.cpp:110:22 行函数前面加
#define STRERROR_R_CHAR_P 1