RPC即遠程過程調用,適用於集群管理,集群節點就是RPCServer,而我們發起遠程調用的web服務器就是RPCClient。所以是少數rpcClient(可能一個)對多個RPCServer(集群節點)。
今天講述的RPC開發希望實現這樣一個效果,在RPCClient上(也就是web服務器)執行一條shell命令,要求指定的遠程主機執行指定的命令。命令的格式如下
rpc_client rpc_server_address command
比如
./ssan_client 192.168.1.1 vmstat
希望這條命令的遠程執行(在RPCServer上)的結果直接在直接輸出到web服務器(RPCClient)。可以先看一個執行效果。
那么要做到這樣一種效果,需要借助RPC框架,這里選擇Apache Thrift, 並且使用C++語言,因為不能保證節點裝什么PHP或JAVA環境,希望最后得到一個與環境無關的可執行文件。
這里首先可以看到Apache官方C++教程
https://thrift.apache.org/tutorial/cpp
下面開始實現rpc。
安裝thrift環境
下載相關文件
yum -y groupinstall "Development Tools" wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz wget http://ftp.gnu.org/gnu/bison/bison-2.5.1.tar.gz wget http://ftp.gnu.org/gnu/automake/automake-1.14.tar.gz wget http://www.mirrorservice.org/sites/dl.sourceforge.net/pub/sourceforge/b/bo/boost/boost/1.55.0/boost_1_55_0.tar.gz git clone https://git-wip-us.apache.org/repos/asf/thrift.git
依次安裝
tar zxf autoconf-2.69.tar.gz cd autoconf-2.69 ./configure --prefix=/usr make sudo make install cd .. tar xvf bison-2.5.1.tar.gz cd bison-2.5.1 ./configure --prefix=/usr make make install cd .. tar zxf automake-1.14.tar.gz cd automake-1.14 ./configure --prefix=/usr make make install cd .. tar zxf boost_1_55_0.tar.gz ./bootstrap.sh ./configure make sudo make install cd ../thrift ./bootstrap.sh yum install openssl openssl-devel -y ./configure make make install
這里在安裝thrift是yum安裝了openssl-dev,是為了解決這個錯誤
安裝完成之后
編寫thrift文件
編寫ssan.thrift文件
namespace cpp ssan service SSANAgent { string run(1:string command) }
這里定義了一個接口方法run,接收一個string類型的參數,表示要執行的命令
使用thrift生成
thrift -r --gen cpp ssan.thrift
生成的源代碼主要在SSANAgent.cpp中,可以翻看SSANAgent.h的定義。
可以看到,我們只需要寫一個類繼承這個類,重寫run方法,在run方法里實現業務,並且可以看到它幫我們添加了一個參數,這個參數用來做什么呢?用來返回結果。Thrift連這事都幫你准備好了
編寫rpcServer
thrift已經為我們生成了server的模板
cp SSANAgent_server.skeleton.cpp SSANServer.cpp
之前定義的接口方法也主要定義在這個類里
修改SSANServer.cpp文件
// This autogenerated skeleton file illustrates how to build a server. // You should copy it to another filename to avoid overwriting it. #include "SSANAgent.h" #include <sstream> #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/server/TSimpleServer.h> #include <thrift/transport/TServerSocket.h> #include <thrift/transport/TBufferTransports.h> using namespace ::apache::thrift; using namespace ::apache::thrift::protocol; using namespace ::apache::thrift::transport; using namespace ::apache::thrift::server; using boost::shared_ptr; using namespace ::ssan; using namespace std; class SSANAgentHandler : virtual public SSANAgentIf { public: SSANAgentHandler() { // Your initialization goes here } void run(std::string& _return, const std::string& command) { std::ostringstream oss; FILE * pp = popen(command.c_str(),"r"); if(pp){ char buf[4096]; while(fgets(buf,sizeof(buf),pp)){ oss << buf; } pclose(pp); } else{ oss << "Error: No such command : " << command; } _return = oss.str(); } }; int main(int argc, char **argv) { int port = 9090; shared_ptr<SSANAgentHandler> handler(new SSANAgentHandler()); shared_ptr<TProcessor> processor(new SSANAgentProcessor(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); cout << "Start SSAN Server ..." << endl; server.serve(); cout << "Done" << endl; return 0; }
主要是在run方法里處理業務
生成rpcServer
先不借助Makefile,依次執行下面的命令編譯
g++ -Wall -I/usr/local/include/thrift -c SSANAgent.cpp g++ -Wall -I/usr/local/include/thrift -c SSANServer.cpp g++ -Wall -I/usr/local/include/thrift -c ssan_constants.cpp g++ -Wall -I/usr/local/include/thrift -c ssan_types.cpp
鏈接文件生成rpcServer,注意這里是動態鏈接
g++ -L/usr/local/lib *.o -o ssan_server –lthrift
鏈接后運行出現這個錯誤
這個文件其實已經存在了,不過在目錄/usr/local/lib/下,而程序運行時再/usr/lib下搜索動態庫文件,所以建立一個軟鏈接
ln -s /usr/local/lib/libthrift-1.0.0-dev.so /usr/lib/libthrift-1.0.0-dev.so
再執行就成功啟動了
編寫rpcClient
rpcClient代碼需要自己創建,頭文件除了server部分,其他地方照抄就是了
#include "SSANAgent.h" #include <ostream> #include <sstream> #include <thrift/protocol/TBinaryProtocol.h> #include <thrift/transport/TServerSocket.h> #include <thrift/transport/TBufferTransports.h> #include <thrift/transport/TSocket.h> using namespace ::apache::thrift; using namespace ::apache::thrift::protocol; using namespace ::apache::thrift::transport; using namespace ssan; using namespace std; int main(int argc,char ** argv){ if(argc < 3){ printf("Usage: %s ip-address command ...\n",argv[0]); return -1; } // 處理輸入參數 ostringstream command,address; address << argv[1]; if(argc > 2){ command << argv[2]; for(int i=3;i < argc;i++){ command << " " << argv[i]; } } // 訪問rpc server執行命令 boost::shared_ptr<TSocket> socket(new TSocket(address.str().c_str(),9090)); boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket)); boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport)); SSANAgentClient client(protocol); string output; transport->open(); client.run(output,command.str()); cout << output << endl; transport->close(); return 0; }
這里實例化了一個SSANAgentClient,它也定義在SSANAgent.h文件中,並且也繼承自SSANAgentIf,所以它也有run方法,並且可以看到它的run方法做了兩件事情,將命令發送給server執行,接收server返回的結果。
生成rpcClient
編譯
g++ -Wall -I/usr/local/include/thrift -c SSANClient.cpp
鏈接
g++ -L/usr/local/lib SSANClient.o SSANAgent.o ssan_constants.o ssan_types.o -o ssan_client -lthrift
這部分基本沒出什么大問題
編寫Makefile編譯
為了更方便的調試代碼,還是需要寫一個Makefile文件。時間有限,這個是臨時寫的,后面會繼續完善
LIB_INC =-L/usr/local/lib SHARE_OBJ =ssan_constants.o ssan_types.o all : server client server: SSANAgent.o SSANServer.o $(SHARE_OBJ) g++ $(LIB_INC) $^ -o ssan_server -lthrift @echo ssan_server created. client: SSANAgent.o SSANClient.o $(SHARE_OBJ) g++ $(LIB_INC) $^ -o ssan_client -lthrift @echo ssan_client created. #default:server client clean : -rm *.o ssan_client ssan_server @echo cleanup done;
執行make,可以看到這樣的效果
至此,RPCServer和RPCClient都已經生成了,下一步就是解決靜態編譯的問題。因為時間有限,這個將會在下一篇博客中PHP對RPC服務的調用封裝中寫出