一:什么是rpcios
rpc通俗来理解就是远程调用函数,相对于本地调用来讲,只须要在主调函数中调用被掉函数便可,代码以下:c++
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(5); 11 return 0; 12 }
在上面的代码中,main( )函数在第10行调用了本地函数fun( ),本地调用就是这么简单。若是要远程调用一个函数,那么就须要进行网络通讯,网络通讯就涉及到了网络编程,网络编程中有一本著名的经典书籍:《UNIX网络编程》,简称UNP,这本书基本上是系统层网络编程人员必读书籍,可是读过这本书的人都知道,网络的细节不少,也较复杂,若是每一个项目都须要亲自写这些底层实现,那无疑大大延缓了项目开发进度,并且不少上层开发人员不懂得这些细节。解决办法就是造轮子,以库的形式封装这些底层细节,屏蔽掉底层,让其余开发人员能够简单直接使用。git
二:thrift的安装golang
thrift 就是前面提到的一种rpc库。它是跨语言的,而且是C/S模式。thrift 的安装与使用过程在其官网是有说明的:https://thrift.apache.org/apache
本文安装过程基于官网教程,安装环境为:Ubuntu 16.04 x64,下面是具体过程:编程
1. 首先须要下载 thrift 工具,下载地址为:https://thrift.apache.org/downloadvim
2. 而后须要编译安装该 thrift 工具,安装教程在此处有详细说明,本文是依据“Debian/Ubuntu install”的安装说明进行的,步骤以下:网络
首先安装 thrift 编译须要的基本依赖组件。架构
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 编译环境请自行参考相关安装教程。socket
接下来开始配置 thrift 工具:
这里下载的是0.10.0版本,测试发现0.11.0版本有一些问题,缺乏某些动态库,另外Python库的编译在Ubuntu上也存在头文件路径错误,而CentOS则没有这个问题。因此建议使用0.10.0版本。
wget "https://mirrors.tuna.tsinghua.edu.cn/apache/thrift/0.10.0/thrift-0.10.0.tar.gz" tar zxvf thrift-0.10.0.tar.gz cd thrift-0.10.0
./configure
若是依赖安装稳当且配置无误,则会看到下面输出:
这里会列出 thrift 工具构建了哪一种语言支持的库,其后还有该语言库相关的详细说明,以下:
接下来开始编译和安装:
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 文件以生成代码,最后将生成的代码集成到项目中使用。架构图以下:
下面是详细步骤:
cd mkdir thrift cd thrift/ vim timeRPC.thrift thrift --gen cpp timeRPC.thrift
以上命令是在个人机器上我的home目录下建立了一个叫作thrift的文件夹并在该文件夹下编写了一个叫作 "timeRPC.thrift" 的 thrift 文件。而后使用 thrift 工具转换该文件生成了 C++ 的服务端代码。
这里的例子很简单,模仿UNIX的daytime服务,提供“时间问答”,容许客户端向服务端询问如今是几点,服务端会把如今的Unix时间回送给客户端。timeRPC.thrift文件的内容以下:
service timeServe {
i32 getCurrtentTime()
}
这里的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 18 class timeServeHandler : virtual public timeServeIf 19 { 20 public: 21 timeServeHandler() 22 { 23 // Your initialization goes here 24 } 25 26 int32_t getCurrtentTime() 27 { 28 // Your implementation goes here 29 auto t = time(nullptr); 30 printf("getCurrtentTime: %ld\n", t); 31 return t; 32 } 33 34 }; 35 36 int main(int argc, char **argv) 37 { 38 int port = 9090; 39 shared_ptr<timeServeHandler> handler(new timeServeHandler()); 40 shared_ptr<TProcessor> processor(new timeServeProcessor(handler)); 41 shared_ptr<TServerTransport> serverTransport(new TServerSocket(port)); 42 shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory()); 43 shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory()); 44 45 TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory); 46 server.serve(); 47 return 0; 48 }
这里须要修改的是第28-31行,这个函数具体实现就是咱们须要的功能,结果从函数返回值得到。这里是调用C标准库的time( )函数来得到一个Unix时间戳,在服务端打印该时间,而且将这个时间戳返回给客户端。
对它进行编译:
g++ -std=c++11 -o cpp-server timeRPC_constants.cpp timeRPC_types.cpp timeServe.cpp main.cpp -lthrift
这里代码使用了C++11的auto推断功能,所以使用-std=c++11选项,另外还须要连接thrift库。结果以下:
C++客户端代码以下:
// system #include <iostream> // lib #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/transport/TSocket.h> #include <thrift/transport/TTransportUtils.h> #include <boost/shared_ptr.hpp> using namespace apache::thrift; using namespace apache::thrift::protocol; using namespace apache::thrift::transport; using boost::shared_ptr; // project #include "gen-cpp/timeServe.h" int main() { // get socket auto p = new TSocket("127.0.0.1", 9090); shared_ptr<TTransport> socket(p); // choose transport:Socket/Http/File auto q = new TBufferedTransport(socket); shared_ptr<TTransport> transport(q); // serialize:Binary/Compact/JSON auto r = new TBinaryProtocol(transport); shared_ptr<TProtocol> protocol(r); timeServeClient client(protocol); // open connect transport->open(); auto timeNow = client.getCurrtentTime(); std::cout << timeNow << std::endl; transport->close(); return 0; }
对它进行编译:
g++ -std=c++11 -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",能够经过下面的命令来安装
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客户端的代码以下:
package main import ( "fmt" "os" "timerpc" "git.apache.org/thrift.git/lib/go/thrift" ) func main() { // get socket socket, err := thrift.NewTSocket("127.0.0.1:9090") // choose transport transport := thrift.NewTBufferedTransport(socket, 8192) // serialize protocolFactory := thrift.NewTBinaryProtocolFactoryDefault() client := timerpc.NewTimeServeClientFactory(transport, protocolFactory) // open connect transport.Open() defer socket.Close() timeResult, err := client.GetCurrtentTime() if err != nil { fmt.Println(err.Error()) os.Exit(2) } fmt.Println(timeResult) }
对它进行编译:
go build client.go
运行程序测试一下结果,以下: