一:什么是rpc

rpc通俗来理解就是远程调用函数,相对于本地调用来说,只需要在主调函数中调用被掉函数即可,代码如下:

  1. void fun(int i)
  2. {
  3. cout << "function call" << endl;
  4. cout << "args: " << i << endl;
  5. return;
  6. }
  7.  
  8. int main()
  9. {
  10. fun();
  11. return ;
  12. }

在上面的代码中,main( )函数在第10行调用了本地函数fun( ),本地调用就是这么简单。如果要远程调用一个函数,那么就需要进行网络通信,网络通信就涉及到了网络编程,网络编程中有一本著名的经典书籍:《UNIX网络编程》,简称UNP,这本书基本上是系统层网络编程人员必读书籍,但是读过这本书的人都知道,网络的细节很多,也较复杂,如果每个项目都需要亲自写这些底层实现,那无疑大大延缓了项目开发进度,而且很多上层开发人员不懂得这些细节。解决办法就是造轮子,以库的形式封装这些底层细节,屏蔽掉底层,让其他开发人员可以简单直接使用。

二:thrift的安装

thrift 就是前面提到的一种rpc库。它是跨语言的,并且是C/S模式。thrift 的安装与使用过程在其官网是有说明的:https://thrift.apache.org/

本文安装过程基于官网教程,安装环境为:Ubuntu 16.04 x64,下面是具体过程:

1. 首先需要下载 thrift 工具,下载地址为:https://thrift.apache.org/download

2. 然后需要编译安装该 thrift 工具,安装教程在此处有详细说明,本文是依据“Debian/Ubuntu install”的安装说明进行的,步骤如下:

首先安装 thrift 编译需要的基本依赖组件。

  1. sudo apt-get install automake bison flex git libboost-all-dev libevent-dev libssl-dev libtool make pkg-config build-essential g++

由于我们是利用 thrift 进行 C++ 与 Go 之间的通信,因此还要安装 Go 编译环境。如果没有安装 Go 编译环境,编译thrift工具时会看到对 Go 的支持为 NO。具体 Go 编译环境请自行参考相关安装教程。

接下来开始配置 thrift 工具:

这里下载的是0.10.0版本,测试发现0.11.0版本有一些问题,缺少某些动态库,另外Python库的编译在Ubuntu上也存在头文件路径错误,而CentOS则没有这个问题。所以建议使用0.10.0版本。

  1. wget "https://mirrors.tuna.tsinghua.edu.cn/apache/thrift/0.10.0/thrift-0.10.0.tar.gz"
  2. tar zxvf thrift-0.10..tar.gz
  3. cd thrift-0.10.0
    ./configure

如果依赖安装妥当且配置无误,则会看到下面输出:

这里会列出 thrift 工具构建了哪种语言支持的库,其后还有该语言库相关的详细说明,如下:

接下来开始编译和安装:

  1. make && make install //如果内存够大、CPU够多,可使用 make -j && make install

由于 configure 配置时没有指定安装的路径,因此这里的 make install 安装到系统默认路径"/usr/local/bin/"下,需要root权限。

需要注意的是,编译 thrift 时,thrift 可能会通过网络利用"go get"工具来安装一些第三方库,如"golang.org/x/net/context",该库由于一些"网络问题"会安装失败,进而导致编译失败,本文利用了命令行的"http_proxy"环境变量设置了相关代理,个人用户请自行准备好代理工具以解决该问题。

安装完成后,thrift工具就可以使用了,如下:

3. 编写 thrift 文件并转换为代码

在安装完成 thrift 工具之后,我们需要用 thrift 定义的语法来编写 thrift 文件,再使用 thrift 工具来转换 thrift 文件以生成代码,最后将生成的代码集成到项目中使用。架构图如下:

下面是详细步骤:

  1. cd
  2. mkdir thrift
  3. cd thrift/
  4. vim timeRPC.thrift
  5. thrift --gen cpp timeRPC.thrift

以上命令是在我的机器上个人home目录下创建了一个叫做thrift的文件夹并在该文件夹下编写了一个叫做 "timeRPC.thrift" 的 thrift 文件。然后使用 thrift 工具转换该文件生成了 C++ 的服务端代码。

这里的例子很简单,模仿UNIX的daytime服务,提供“时间问答”,允许客户端向服务端询问现在是几点,服务端会把现在的Unix时间回送给客户端。timeRPC.thrift文件的内容如下:

  1. service timeServe {
  2. i32 getCurrtentTime()
  3. }

这里的service相当于C++的类、Go的包,而getCurrentTime( )是对应的成员/包函数,函数体内是具体的功能实现。

如果上面的转换操作成功,会在当前目录下生成一个叫做 "gen-cpp" 的文件夹,里面包含了需要的代码,如下图:

总共生成了7个文件,其中最后一个文件是我们的"main.cpp"文件,我们需要对它进行适当修改以使用。

先重命名一下,如下图:

三:利用thrift进行C++与Go通信

在经过前面步骤之后,我们已经得到了C++版本的代码,这些代码需要分别引入服务端和客户端以进行通信使用。

在前面我们已经得到了C++服务端的代码,服务端代码的源文件被重名成了"main.cpp",代码内容如下:

  1. // This autogenerated skeleton file illustrates how to build a server.
  2. // You should copy it to another filename to avoid overwriting it.
  3.  
  4. #include "timeServe.h"
  5. #include <thrift/protocol/TBinaryProtocol.h>
  6. #include <thrift/server/TSimpleServer.h>
  7. #include <thrift/transport/TServerSocket.h>
  8. #include <thrift/transport/TBufferTransports.h>
  9.  
  10. using namespace ::apache::thrift;
  11. using namespace ::apache::thrift::protocol;
  12. using namespace ::apache::thrift::transport;
  13. using namespace ::apache::thrift::server;
  14.  
  15. using boost::shared_ptr;
  16.  
  17. class timeServeHandler : virtual public timeServeIf
  18. {
  19. public:
  20. timeServeHandler()
  21. {
  22. // Your initialization goes here
  23. }
  24.  
  25. int32_t getCurrtentTime()
  26. {
  27. // Your implementation goes here
  28. auto t = time(nullptr);
  29. printf("getCurrtentTime: %ld\n", t);
  30. return t;
  31. }
  32.  
  33. };
  34.  
  35. int main(int argc, char **argv)
  36. {
  37. int port = ;
  38. shared_ptr<timeServeHandler> handler(new timeServeHandler());
  39. shared_ptr<TProcessor> processor(new timeServeProcessor(handler));
  40. shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  41. shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
  42. shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
  43.  
  44. TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
  45. server.serve();
  46. return ;
  47. } 

这里需要修改的是第28-31行,这个函数具体实现就是我们需要的功能,结果从函数返回值获得。这里是调用C标准库的time( )函数来获得一个Unix时间戳,在服务端打印该时间,并且将这个时间戳返回给客户端。

对它进行编译:

  1. g++ -std=c++ -o cpp-server timeRPC_constants.cpp timeRPC_types.cpp timeServe.cpp main.cpp -lthrift

这里代码使用了C++11的auto推断功能,因此使用-std=c++11选项,另外还需要链接thrift库。结果如下:

C++客户端代码如下:

  1. // system
  2. #include <iostream>
  3.  
  4. // lib
  5. #include <thrift/protocol/TBinaryProtocol.h>
  6. #include <thrift/transport/TSocket.h>
  7. #include <thrift/transport/TTransportUtils.h>
  8. #include <boost/shared_ptr.hpp>
  9. using namespace apache::thrift;
  10. using namespace apache::thrift::protocol;
  11. using namespace apache::thrift::transport;
  12. using boost::shared_ptr;
  13.  
  14. // project
  15. #include "gen-cpp/timeServe.h"
  16.  
  17. int main() {
  18. // get socket
  19. auto p = new TSocket("127.0.0.1", );
  20. shared_ptr<TTransport> socket(p);
  21.  
  22. // choose transport:Socket/Http/File
  23. auto q = new TBufferedTransport(socket);
  24. shared_ptr<TTransport> transport(q);
  25.  
  26. // serialize:Binary/Compact/JSON
  27. auto r = new TBinaryProtocol(transport);
  28. shared_ptr<TProtocol> protocol(r);
  29.  
  30. timeServeClient client(protocol);
  31.  
  32. // open connect
  33. transport->open();
  34. auto timeNow = client.getCurrtentTime();
  35. std::cout << timeNow << std::endl;
  36. transport->close();
  37.  
  38. return ;
  39. }

对它进行编译:

  1. g++ -std=c++ -o cpp-client client.cpp gen-cpp/timeServe.cpp -lthrift

这里在gen-cpp的上一层目录中创建了client.cpp文件,代码中使用了C++11的auto推断功能和智能指针,因此使用-std=c++11选项,另外同样需要链接thrift库。结果如下:

到这里为止,已经完成了通过thrift进行C++客户端和服务端之间的通信,运行程序测试一下结果,如下:

从上面的截图可以看到,运行服务端程序之后,通过客户端程序去请求,可以得到服务端返回的时间戳,并且服务端同时也进行了打印。

接下来使用golang语言来实现客户端。类似C++需要链接thrift库一样,golang也需要对应的thrift的package来提供支持。这个包叫做"git.apache.org/thrift.git/lib/go/thrift",可以通过下面的命令来安装

  1. go get git.apache.org/thrift.git/lib/go/thrift

需要注意的是,这个地址被墙了,因此需要代理访问。当然,最终安装的thrift包可能会编译出错。报类似这样的错误:"not enough arguments in call to oprot.Flush",之所以报这个错是apache更新了thrift的接口,不兼容导致。因此,我们也可以不安装上面的包,而是使用最前面编译得到的golang包。如下:

注意拷贝thrift包的时候,要正确创建package的目录,因为后面生成的服务代码中导入的包路径对此有要求。

除了golang的包需要拷贝到开发目录下,我们也需要生成特定的服务代码,如下:

然后再将生成的服务代码拷贝到go开发环境目录下,然后创建一个go类型的客户端,如下:

client.go客户端的代码如下:

  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "os"
  6. "timerpc"
  7.  
  8. "git.apache.org/thrift.git/lib/go/thrift"
  9. )
  10.  
  11. func main() {
  12. // get socket
  13. socket, err := thrift.NewTSocket("127.0.0.1:9090")
  14.  
  15. // choose transport
  16. transport := thrift.NewTBufferedTransport(socket, 8192)
  17.  
  18. // serialize
  19. protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
  20.  
  21. client := timerpc.NewTimeServeClientFactory(transport, protocolFactory)
  22.  
  23. // open connect
  24. transport.Open()
  25. defer socket.Close()
  26.  
  27. timeResult, err := client.GetCurrtentTime()
  28. if err != nil {
  29. fmt.Println(err.Error())
  30. os.Exit(2)
  31. }
  32. fmt.Println(timeResult)
  33.  
  34. }

对它进行编译:

  1. go build client.go

运行程序测试一下结果,如下:

利用thrift rpc进行C++与Go的通信的更多相关文章

  1. thrift RPC 框架的自我搭建

    安装thrift rpc   安装的系统是Centos 7 未成功的方法 :(原因没找到,但是还是要记录下) 安装依赖库 yum install automake libtool flex bison ...

  2. thrift rpc通信

    thrift rpc通信 框架 别人的简历: 负责抓取程序的开发和维护,对抓取内容进行数据提取.整理.1.定向数据抓取程序的维护和开发,了解了Sqlite数据库.Thrift服务和多线程的开发调试.2 ...

  3. Thrift RPC Golang、C++ Example

    Thrift RPC Example 运行 请直接使用即可,无需拉取任何依赖包. cd $GOPATH/src git clone https://github.com/hunterhug/thrif ...

  4. Thrift RPC实战(三) thrift序列化揭秘

    本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...

  5. Vue中利用$emit实现子组件向父组件通信

    Vue中利用$emit实现子组件向父组件通信 父组件 <template> <div> <p>我是父组件</p> <child :isShow=& ...

  6. Thrift RPC实战(一).初次体验Thrift

    1.前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码,主要特点: 开发速度快: 通过编写RPC接口ID ...

  7. 性能工具之Jmeter压测Thrift RPC服务

    概述 Thrift是一个可互操作和可伸缩服务的框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, ...

  8. Thrift RPC改进—更加准确的超时管理

    前言: 之前我们组内部使用Thrift搭建了一个小型的RPC框架,具体的实现细节可以参考我之前的一篇技术文章:https://www.cnblogs.com/kaiblog/p/9507642.htm ...

  9. Thrift 个人实战--Thrift RPC服务框架日志的优化

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

随机推荐

  1. 动态生成lookup字段

    var  i: Integer;begin  //ADOQuery已打开   //在数据集打开的情况下新增加一个字段  with Self.ADOQuery1 do  begin    TDataSe ...

  2. linux系统日志__ratelimit: N callbacks suppressed

    报错 今天线上遇到故障,php进行因为段错误退出了,系统日志中的kernel报错如下: Feb 25 22:25:11 web_server_01 kernel: __ratelimit: 250 c ...

  3. D-Separation(D分离)-PRML-8.22-Graphical Model 五 18 by 小军

    D-Separation(D分离)-PRML-8.22-Graphical Model 五18by 小军   一.引言 在贝叶斯网络的学习过程中,经常会遇到(D-Separation)D-分离这个概念 ...

  4. Day24-Ajax文件上传

    一. <input type="file" id="fafafa" name="afafaf"/> <input type ...

  5. c++11 线程的互斥量

    c++11 线程的互斥量 为什么需要互斥量 在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源.这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在 ...

  6. 【刷题】LOJ 6000 「网络流 24 题」搭配飞行员

    题目描述 飞行大队有若干个来自各地的驾驶员,专门驾驶一种型号的飞机,这种飞机每架有两个驾驶员,需一个正驾驶员和一个副驾驶员.由于种种原因,例如相互配合的问题,有些驾驶员不能在同一架飞机上飞行,问如何搭 ...

  7. 《Linux内核设计与实现》学习总结 Chap5

    一.与内核通信 1.系统调用在用户空间进程和硬件设备之间添加了一个中间层. 作用: 1)为用户空间提供了一种硬件的抽象接口. 2)系统调用保证了系统的稳定和安全. 3)每个进程都运行在虚拟系统中,而在 ...

  8. myeclipse2015修改web项目部署名

    在旧版本的myeclipse中修改web项目部署名很方便,直接右键在properties中查找web就能进行修改. 但是myeclipse2015中发现不能直接修改了. 我们可以点击config,或者 ...

  9. java多线程 -- volatile 关键字 内存 可见性

    内存可见性(Memory Visibility) 1 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其 ...

  10. 解题:POI 2009 Ticket Inspector

    题面 看起来很水,然而不会DP的蒟蒻并不会做,PoPoqqq orz 设$f[i][j]$表示当前在第$i$个点和第$i+1$个点之间查票,已经查了$j$次的最大收益.然后就是那种很常见的枚举前一个结 ...