thrift简单示例 (基于C++)
这个thrift的简单示例, 来源于官网 (http://thrift.apache.org/tutorial/cpp), 因为我觉得官网的例子已经很简单了, 所以没有写新的示例, 关于安装的教程, 可以参考https://www.cnblogs.com/albizzia/p/10838646.html, 关于thrift文件的语法, 可以参考: https://www.cnblogs.com/albizzia/p/10838646.html.
thrift文件
首先给出shared.thrift文件的定义:
/**
* 这个Thrift文件包含一些共享定义
*/ namespace cpp shared struct SharedStruct {
1: i32 key
2: string value
} service SharedService {
SharedStruct getStruct(1: i32 key)
}
然后给出tutorial.thrift的定义:
/**
* Thrift引用其他thrift文件, 这些文件可以从当前目录中找到, 或者使用-I的编译器参数指示.
* 引入的thrift文件中的对象, 使用被引入thrift文件的名字作为前缀, 例如shared.SharedStruct.
*/
include "shared.thrift" namespace cpp tutorial // 定义别名
typedef i32 MyInteger /**
* 定义常量. 复杂的类型和结构体使用JSON表示法.
*/
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} /**
* 枚举是32位数字, 如果没有显式指定值,从1开始.
*/
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
} /**
* 结构体由一组成员来组成, 一个成员包括数字标识符, 类型, 符号名, 和一个可选的默认值.
* 成员可以加"optional"修饰符, 用来表明如果这个值没有被设置, 那么他们不会被串行化到
* 结果中. 不过这个在有些语言中, 需要显式控制.
*/
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment,
} // 结构体也可以作为异常
exception InvalidOperation {
1: i32 whatOp,
2: string why
} /**
* 服务需要一个服务名, 加上一个可选的继承, 使用extends关键字
*/
service Calculator extends shared.SharedService { /**
* 方法定义和C语言一样, 有返回值, 参数或者一些它可能抛出的异常, 参数列表和异常列表的
* 写法与结构体中的成员列表定义一致.
*/ void ping(), i32 add(1:i32 num1, 2:i32 num2), i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), /**
* 这个方法有oneway修饰符, 表示客户段发送一个请求, 然后不会等待回应. Oneway方法
* 的返回值必须是void
*/
oneway void zip() }
将上述文件放置在同一个文件夹, 然后编译上述的thrift文件:
$ thrift -r --gen cpp tutorial.thrift
生成的文件会出现在gen-cpp子文件夹中, 因为编译时使用了-r参数, 所以shared.thrift也会被编译.
我们可以考虑查看一下thrift编译之后生成的文件, 这里, 我们可以考虑先编译shared.thrift, 编译后, 会生成7个文件, 分别是shared_constants.h, shared_constants.cpp, shared_types.h, shared_types.cpp, SharedService.h, SharedService.cpp, SharedService_server.skeleton.cpp.
我们先查看shared_constants.h和shared.constants.cpp, 这两个文件的命名是原先的thrift文件名, 加上_constants, 换种方式说, 就是xxx.thrift会生成xxx_constants.h和xxx_constants.cpp. 查看一下这两个文件中的内容: 其中会有一个类叫做xxxConstants, 在这个类中, 会将常量作为公有成员, 然后可以通过一个全局变量g_xxx_constants访问. 而xxx_constants.cpp为类函数的定义, 以及全局变量的定义, 应该比较容易理解.
关于shared_types.h和shared_types.cpp文件, 查看shared_types.h中的内容可以看出, shared_types.h中是thrift文件中各种类型的定义, 这个根据文件名应该可以大致猜出. 其中每一个结构体对应两部分, 假设这个结构体叫yyy, 那么第一个部分是结构体_yyy__isset,这个结构体会为thrift中yyy的每个字段添加一个对应的bool值, 名字相同. 第二部分是结构体yyy. 这个结构体中包括thrift中yyy的每个字段, 加上_yyy__isset对象. 这个对象用于yyy读取输入给自身赋值时, 标识某个字段是否被赋值. yyy中的函数主要有如下几个: (1) 默认构造函数 (2) 析构函数 (3) 设置成员变量值的函数 (4) 比较函数 (5) read函数, 用来读取TProtocol对象的内容, 来给自己赋值 (6) write函数, 将自身的值写入到TProtocol的对象中. 最后还有一个swap函数.
关于SharedService.h和SharedService.cpp文件, 查看SharedService.h中的内容可以看出, 这个文件的文件名来自于thrift中的service SharedService, 假设服务名叫做zzz, 那么就会生成对应的zzz.h和zzz.cpp文件, 用来实现这个服务的接口相关的内容. 查看SharedService.h文件, 可以看到如下内容:
(1) class SharedServiceIf用来实现thrift文件中SharedService的接口,
(2) SharedServiceIfFactory用来实现SharedServiceIf的工厂接口, 构建函数为getHandler, 释放函数为releaseHandler, 其中SharedServiceIf在工厂类中定义别名Handler.
(3) SharedServiceIfSingletonFactory是SharedServiceIfFactory的一个具体实现, 用来实现返回单例的SharedServiceIf对象.
(4) SharedServiceNull是SharedServiceIf的不做任何行为的实现.
(5) _SharedService_getStruct_args__isset是SharedService服务的getStruct函数的参数对应的isset类, 用来表示这些参数是否存在.
(6) SharedService_getStruct_args是SharedService服务的getStruct函数的参数对应的类, 用来表示一个服务的函数的参数, 实现内容和thrift文件中的结构体的实现基本一致.
(7) SharedService_getStruct_pargs用处不太清楚.
(8) _SharedService_getStruct_result__isset是SharedService服务的getStruct函数的返回值对应的isset函数, 用来表示返回值是否设置.
(9) SharedService_getStruct_result是SharedService服务的getStruct函数的返回值对应的类, 用来表示一个服务的函数的返回值.
(10) _SharedService_getStruct_presult__isset和SharedService_getStruct_presult用处不太清楚.
(11) SharedServiceClient 是thrift中SharedService服务的客户端实现. SharedServiceClient包括以下内容:
1) 构造函数
2) 获取输入和输出Protocol的函数
3) 服务中定义的方法, 这里是getStruct函数, 以及getStruct函数实现的两个函数,
void SharedServiceClient::getStruct(SharedStruct& _return, const int32_t key)
{
send_getStruct(key);
recv_getStruct(_return);
}
(12) SharedServiceProcessor为编译器自动生成的对象, 位于Protocol层之上, Server层之下, 实现从输入protocol中读取数据, 然后交给具体的Handler处理, 然后再将结果写入到输出protocol中. 关于这些联系可以参考 https://www.cnblogs.com/albizzia/p/10884907.html.
(13) SharedServiceProcessorFactory是SharedServiceProcessor的工厂类.
(14) SharedServiceMultiface是SharedServiceIf的具体实现, 实现了类似于chain of responsiblity的效果, 也就是依次调用构造函数中传入的多个
SharedServiceIf对象的对应方法. 参考代码:
void getStruct(SharedStruct& _return, const int32_t key) {
size_t sz = ifaces_.size();
size_t i = ;
for (; i < (sz - ); ++i) {
ifaces_[i]->getStruct(_return, key);
}
ifaces_[i]->getStruct(_return, key);
return;
}
关于SharedService_server.skeleton.cpp文件, 假设thrift中定义的服务名叫做zzz, 那么这个文件名叫做zzz_server.skeleton.cpp, skeleton的含义是框架, 这个文件的作用是告诉我们如何写出thrift服务器的代码. 这个文件包括两部分:
(1) 类SharedServiceHandler, 这个类用来实现SharedServiceIf, 假设thrift中的服务名叫做zzz, 那么这个类的名字就相对的叫做zzzHandler. 这个类会给出如果你想要实现SharedServiceIf, 那么你需要实现的具体的函数, 对于这个类来说, 需要实现构造函数和getStruct函数, getStruct函数是服务中定义的函数, 有时候, 你也需要实现析构函数吧.
(2) 然后是一个main函数, main函数中的内容, 告诉你怎样实现一个简单的thrift服务器. 你可以考虑把这个文件拷贝一份, 然后根据这个拷贝进行修改, 实现服务器的功能.
如果把shared.thrift和tutorial.thrift一起编译, 那么就会出现14个文件, 每个thrift文件对应7个, 文件的布局和作用和之前说明的一致.
服务端代码
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>
#include <thrift/TToString.h>
#include <thrift/stdcxx.h> #include <iostream>
#include <stdexcept>
#include <sstream> #include "../gen-cpp/Calculator.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 tutorial;
using namespace shared; class CalculatorHandler : public CalculatorIf {
public:
CalculatorHandler() {} void ping() { cout << "ping()" << endl; } int32_t add(const int32_t n1, const int32_t n2) {
cout << "add(" << n1 << ", " << n2 << ")" << endl;
return n1 + n2;
} int32_t calculate(const int32_t logid, const Work& work) {
cout << "calculate(" << logid << ", " << work << ")" << endl;
int32_t val; switch (work.op) {
case Operation::ADD:
val = work.num1 + work.num2;
break;
case Operation::SUBTRACT:
val = work.num1 - work.num2;
break;
case Operation::MULTIPLY:
val = work.num1 * work.num2;
break;
case Operation::DIVIDE:
if (work.num2 == ) {
InvalidOperation io;
io.whatOp = work.op;
io.why = "Cannot divide by 0";
throw io;
}
val = work.num1 / work.num2;
break;
default:
InvalidOperation io;
io.whatOp = work.op;
io.why = "Invalid Operation";
throw io;
} SharedStruct ss;
ss.key = logid;
ss.value = to_string(val); log[logid] = ss; return val;
} void getStruct(SharedStruct& ret, const int32_t logid) {
cout << "getStruct(" << logid << ")" << endl;
ret = log[logid];
} void zip() { cout << "zip()" << endl; } protected:
map<int32_t, SharedStruct> log;
}; /*
CalculatorIfFactory is code generated.
CalculatorCloneFactory is useful for getting access to the server side of the
transport. It is also useful for making per-connection state. Without this
CloneFactory, all connections will end up sharing the same handler instance.
*/
class CalculatorCloneFactory : virtual public CalculatorIfFactory {
public:
virtual ~CalculatorCloneFactory() {}
virtual CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo)
{
stdcxx::shared_ptr<TSocket> sock = stdcxx::dynamic_pointer_cast<TSocket>(connInfo.transport);
cout << "Incoming connection\n";
cout << "\tSocketInfo: " << sock->getSocketInfo() << "\n";
cout << "\tPeerHost: " << sock->getPeerHost() << "\n";
cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
cout << "\tPeerPort: " << sock->getPeerPort() << "\n";
return new CalculatorHandler;
}
virtual void releaseHandler( ::shared::SharedServiceIf* handler) {
delete handler;
}
}; int main() {
TThreadedServer server(
stdcxx::make_shared<CalculatorProcessorFactory>(stdcxx::make_shared<CalculatorCloneFactory>()),
stdcxx::make_shared<TServerSocket>(), //port
stdcxx::make_shared<TBufferedTransportFactory>(),
stdcxx::make_shared<TBinaryProtocolFactory>()); /*
// if you don't need per-connection state, do the following instead
TThreadedServer server(
stdcxx::make_shared<CalculatorProcessor>(stdcxx::make_shared<CalculatorHandler>()),
stdcxx::make_shared<TServerSocket>(9090), //port
stdcxx::make_shared<TBufferedTransportFactory>(),
stdcxx::make_shared<TBinaryProtocolFactory>());
*/ /**
* Here are some alternate server types... // This server only allows one connection at a time, but spawns no threads
TSimpleServer server(
stdcxx::make_shared<CalculatorProcessor>(stdcxx::make_shared<CalculatorHandler>()),
stdcxx::make_shared<TServerSocket>(9090),
stdcxx::make_shared<TBufferedTransportFactory>(),
stdcxx::make_shared<TBinaryProtocolFactory>()); const int workerCount = 4; stdcxx::shared_ptr<ThreadManager> threadManager =
ThreadManager::newSimpleThreadManager(workerCount);
threadManager->threadFactory(
stdcxx::make_shared<PlatformThreadFactory>());
threadManager->start(); // This server allows "workerCount" connection at a time, and reuses threads
TThreadPoolServer server(
stdcxx::make_shared<CalculatorProcessorFactory>(stdcxx::make_shared<CalculatorCloneFactory>()),
stdcxx::make_shared<TServerSocket>(9090),
stdcxx::make_shared<TBufferedTransportFactory>(),
stdcxx::make_shared<TBinaryProtocolFactory>(),
threadManager);
*/ cout << "Starting the server..." << endl;
server.serve();
cout << "Done." << endl;
return ;
}
上述代码应该很容易理解, 在这里就不解释了.
客户端代码
#include <iostream> #include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>
#include <thrift/stdcxx.h> #include "../gen-cpp/Calculator.h" using namespace std;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport; using namespace tutorial;
using namespace shared; int main() {
stdcxx::shared_ptr<TTransport> socket(new TSocket("localhost", ));
stdcxx::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
stdcxx::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
CalculatorClient client(protocol); try {
transport->open(); client.ping();
cout << "ping()" << endl; cout << "1 + 1 = " << client.add(, ) << endl; Work work;
work.op = Operation::DIVIDE;
work.num1 = ;
work.num2 = ; try {
client.calculate(, work);
cout << "Whoa? We can divide by zero!" << endl;
} catch (InvalidOperation& io) {
cout << "InvalidOperation: " << io.why << endl;
// or using generated operator<<: cout << io << endl;
// or by using std::exception native method what(): cout << io.what() << endl;
} work.op = Operation::SUBTRACT;
work.num1 = ;
work.num2 = ;
int32_t diff = client.calculate(, work);
cout << "15 - 10 = " << diff << endl; // Note that C++ uses return by reference for complex types to avoid
// costly copy construction
SharedStruct ss;
client.getStruct(ss, );
cout << "Received log: " << ss << endl; transport->close();
} catch (TException& tx) {
cout << "ERROR: " << tx.what() << endl;
}
}
从上面的客户端调用来看, 方法调用和本地的类对象的调用很相似, thrift的设计算是很巧妙的. 里面的代码应该不复杂, 所以也不进行具体的讲解了.
查看一下CMakeLists.txt文件:
cmake_minimum_required(VERSION 2.8) #include_directories(SYSTEM "${Boost_INCLUDE_DIRS}") #Make sure gen-cpp files can be included
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen-cpp")
include_directories("${PROJECT_SOURCE_DIR}/lib/cpp/src") set(tutorialgencpp_SOURCES
gen-cpp/Calculator.cpp
gen-cpp/SharedService.cpp
gen-cpp/shared_constants.cpp
gen-cpp/shared_types.cpp
gen-cpp/tutorial_constants.cpp
gen-cpp/tutorial_types.cpp
)
add_library(tutorialgencpp STATIC ${tutorialgencpp_SOURCES})
target_link_libraries(tutorialgencpp thrift) add_custom_command(OUTPUT gen-cpp/Calculator.cpp gen-cpp/SharedService.cpp gen-cpp/shared_constants.cpp gen-cpp/shared_types.cpp gen-cpp/tutorial_constants.cpp gen-cpp/tutorial_types.cpp
COMMAND ${THRIFT_COMPILER} --gen cpp -r ${PROJECT_SOURCE_DIR}/tutorial/tutorial.thrift
) add_executable(TutorialServer CppServer.cpp)
target_link_libraries(TutorialServer tutorialgencpp)
if (ZLIB_FOUND)
target_link_libraries(TutorialServer ${ZLIB_LIBRARIES})
endif () add_executable(TutorialClient CppClient.cpp)
target_link_libraries(TutorialClient tutorialgencpp)
if (ZLIB_FOUND)
target_link_libraries(TutorialClient ${ZLIB_LIBRARIES})
endif ()
编译运行, 我这边启动客户端和服务端的命令分别是:
$ LD_LIBRARY_PATH=/usr/local/lib ./TutorialServer
$ LD_LIBRARY_PATH=/usr/local/lib ./TutorialClient
注: 上述代码可以在thrift源代码中的tutorial/cpp文件夹找到.
thrift简单示例 (基于C++)的更多相关文章
- Python Thrift 简单示例
本文基于Thrift-0.10,使用Python实现服务器端,使用Java实现客户端,演示了Thrift RPC调用示例.Java客户端提供两个字符串参数,Python服务器端计算这两个字符串的相似度 ...
- DotNetty关键概念及简单示例(基于NET5)
DotNetty关键概念及简单示例(基于NET5) 目录 DotNetty关键概念及简单示例(基于NET5) 1.DotNetty 设计的关键 1.1 核心组件 1.1.1 Channel 1.1.2 ...
- thrift简单示例 (go语言)
这个thrift的简单示例来自于官网 (http://thrift.apache.org/tutorial/go), 因为官方提供的例子简单易懂, 所以没有必要额外考虑新的例子. 关于安装的教程, 可 ...
- 最简单的基于Flash的流媒体示例:网页播放器(HTTP,RTMP,HLS)
http://blog.csdn.net/leixiaohua1020/article/details/43936415 ======================================= ...
- 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...
- 最简单的基于Flash的流媒体示例:RTMP推送和接收(ActionScript)
===================================================== Flash流媒体文章列表: 最简单的基于Flash的流媒体示例:RTMP推送和接收(Acti ...
- 最简单的基于DirectShow的示例:获取Filter信息
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于DirectShow的示例:视频播放器自定义版
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 最简单的基于DirectShow的示例:视频播放器图形界面版
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
随机推荐
- docker 运行 sqlserver
docker 运行 sqlserver docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=sa123456' -e 'MSSQL_PID=Deve ...
- springboot 读取配置文件
读取配置文件 在以前的项目中我们主要在 XML 文件中进行框架配置,业务的相关配置会放在属性文件中,然后通过一个属性读取的工具类来读取配置信息. 在 Spring Boot 中我们不再需要使用这种方式 ...
- EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器和EasyDSS云平台异同
背景分析 不同于EasyDSS流媒体服务器与EasyDSS流媒体解决方案(EasyDSS流媒体解决方案就是通过EasyDSS流媒体服务器完善业务层研发而来),EasyDSS流媒体服务器和EasyDSS ...
- Mac下 homebrew 安装mysql
操作步骤 安装homebrew brew install mysql 安装mysql 安装完成之后,可以运行命令启动mysql服务 mysql.server start 然后输入命令设置密码 mysq ...
- linux 程序失败自动重启
最近写了一个spark streaming 程序,但是程序跑着跑着就报错了,而且不会自动重启,以下脚本实现了程序失败自动重启 基本原理:查看程序日志文件是否有ERROR或Exception字样,有说明 ...
- Servlet3.0对异步处理的支持
Servlet工作流程 Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下: Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理: 调用业务接口的某些方 ...
- SQL Server 数据库启动过程,以及启动不起来的各种问题的分析及解决技巧
目前SQL Server数据库作为微软一款优秀的RDBMS,其本身启动的时候是很少出问题的,我们在平时用的时候,很少关注起启动过程,或者很少了解其底层运行过程,大部分的过程只关注其内部的表.存储过程. ...
- [转帖](区块链补习班)ERC20很多人都听过,但ERC是什么你真的了解吗?
(区块链补习班)ERC20很多人都听过,但ERC是什么你真的了解吗? http://baijiahao.baidu.com/s?id=1600948969290990883&wfr=spide ...
- [转帖][区块链]共识算法(POW,POS,DPOS,PBFT)介绍和心得
[区块链]共识算法(POW,POS,DPOS,PBFT)介绍和心得 置顶 2017-03-12 18:31:19 乐扣老师lekkoliu 阅读数 127953 收藏 更多 分类专栏: 技术管理 区 ...
- Java基础---Java环境配置
java 下载:https://www.java.com/zh_CN/ 1.Java安装:jdk9 2. JAVA_HOME 环境变量的配置 在DOS命令行下使用这些工具,就要先进入到JDK的bin目 ...