这个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++)的更多相关文章

  1. Python Thrift 简单示例

    本文基于Thrift-0.10,使用Python实现服务器端,使用Java实现客户端,演示了Thrift RPC调用示例.Java客户端提供两个字符串参数,Python服务器端计算这两个字符串的相似度 ...

  2. DotNetty关键概念及简单示例(基于NET5)

    DotNetty关键概念及简单示例(基于NET5) 目录 DotNetty关键概念及简单示例(基于NET5) 1.DotNetty 设计的关键 1.1 核心组件 1.1.1 Channel 1.1.2 ...

  3. thrift简单示例 (go语言)

    这个thrift的简单示例来自于官网 (http://thrift.apache.org/tutorial/go), 因为官方提供的例子简单易懂, 所以没有必要额外考虑新的例子. 关于安装的教程, 可 ...

  4. 最简单的基于Flash的流媒体示例:网页播放器(HTTP,RTMP,HLS)

    http://blog.csdn.net/leixiaohua1020/article/details/43936415 ======================================= ...

  5. 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)

    一.前言 至今为止编程开发已经11个年头,从 VB6.0,ASP时代到ASP.NET再到MVC, 从中见证了.NET技术发展,从无畏无知的懵懂少年,到现在的中年大叔,从中的酸甜苦辣也只有本人自知.随着 ...

  6. 最简单的基于Flash的流媒体示例:RTMP推送和接收(ActionScript)

    ===================================================== Flash流媒体文章列表: 最简单的基于Flash的流媒体示例:RTMP推送和接收(Acti ...

  7. 最简单的基于DirectShow的示例:获取Filter信息

    ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...

  8. 最简单的基于DirectShow的示例:视频播放器自定义版

    ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...

  9. 最简单的基于DirectShow的示例:视频播放器图形界面版

    ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...

随机推荐

  1. git解决error: The following untracked working tree files would be overwritten by checkout

    在IDEA中进行分支切换时,出现如此错误,导致无法正常切换:error: The following untracked working tree files would be overwritten ...

  2. Android Studio 3.0——unable to resolve dependency for cordovalib

    Android Studio 3.0 更新了gradle后,项目竟然开始报错unable to resolve dependency for cordovalib...打开build.gradle看了 ...

  3. Jmeter+nfluxDB+Grafana性能监控平台

    下载地址: nfluxDB下载地址:https://portal.influxdata.com/downloads/ Grafana下载地址:https://grafana.com/grafana/d ...

  4. squid4

    主机上的squid一直是傻瓜型使用,yum安装.默认配置.千年不动.突然漏扫出来3.X版本不能用了,搜了下,得升4.神奇的发现centos7的源(阿里源)里面竟然最高只有3.网上搜使用yum装的也都是 ...

  5. 快速修改Windows系统密码命令

    因现场需要,要对30多台虚拟机进行密码修改.正常修改方式为进入控制面板--用户账户--修改密码,输入原始密码.2遍新密码(一遍用于密码确认)完成密码修改. 这种方式操作较为繁琐,我们可以直接通过命令的 ...

  6. 【剑指offer】数组中只出现一次的数

    题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 分析: 经典的异或技巧题 两个相同的数字异或的结果为0,一个数和0异或的结果是其本身,假设现在那 ...

  7. WSAEventSelect模型

    WSAEventSelect模型 EventSelect WSAEventSelect function The WSAEventSelect function specifies an event ...

  8. Vue框架(一)——Vue导读、Vue实例(挂载点el、数据data、过滤器filters)、Vue指令(文本指令v-text、事件指令v-on、属性指令v-bind、表单指令v-model)

    Vue导读 1.Vue框架 vue是可以独立完成前后端分离式web项目的js框架 三大主流框架之一:Angular.React.Vue vue:结合其他框架优点.轻量级.中文API.数据驱动.双向绑定 ...

  9. 1. Spark GraphX概述

    1.1 什么是Spark GraphX Spark GraphX是一个分布式图处理框架,它是基于Spark平台提供对图计算和图挖掘简洁易用的而丰富的接口,极大的方便了对分布式图处理的需求.那么什么是图 ...

  10. python_操作linux上的mysql

    在编写初期,遇见一个问题,发现怎么连接不上mysql,一直报错1045: 最后发现,只要下面的,连接写正确,不会出现这个问题, 只要你保证你的user.pwd是正确的, import pymysqld ...