本章我们简单介绍一下gRPC的安装及使用

1. gRPC的安装

1) 安装protobuf

关于protobuf的安装,我们前面介绍过,这里不再赘述。

2) 安装依赖库libcares

在安装gRPC时我们发现需要依赖libcares库,但是gRPC其本身的编译脚本不能很好的安装该库,这里我们手动安装:

# wget https://c-ares.haxx.se/download/c-ares-1.15.0.tar.gz
# tar -zxvf c-ares-1.15.0.tar.gz 
# cd c-ares-1.15.0/
# ./configure --prefix=/usr/local/cares
# make
# make install
# ls /usr/local/cares
include  lib  share

3) 安装perftools

gperftools依赖于libunwind,首先执行如下命令安装:

# wget http://download.savannah.nongnu.org/releases/libunwind/libunwind-1.3.1.tar.gz
# tar -zxvf libunwind-1.3.1.tar.gz 
# cd libunwind-1.3.1/
./configure --prefix=/usr/local/unwind
# make
# make install
# ls /usr/local/unwind/
include  lib

接着再安装google-perfile:

# wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.7/gperftools-2.7.tar.gz
# tar -zxvf gperftools-2.7.tar.gz 
# cd gperftools-2.7/
# ./configure --prefix=/usr/local/gperftools
# make
# make install
# ls /usr/local/gperftools/
bin  include  lib  share

3) 安装gRPC

这里我们先下载gRPC安装软件包并解压:

# mkdir gRPC-inst
# cd gRPC-inst
# wget https://github.com/grpc/grpc/archive/v1.22.0.tar.gz
# tar -zxvf v1.22.0.tar.gz 
# cd grpc-1.22.0/

由于gRPC编译安装脚本写的不是很好,在执行make run_dep_checks时会通过pkg-config来识别编译时所依赖的库是否存在,而真正在执行make时却又会识别不了,因此我们导出如下几个环境变量:

# export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/protobuf/bin/
# export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/:/usr/local/cares/lib/pkgconfig/:/usr/local/gperftools/lib/pkgconfig/      //编译时
# export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/:/usr/local/cares/lib/:/usr/local/gperftools/lib/               //编译时

修改Makefile文件,在PERFTOOLS_CHECK_CMD命令最后加上pkg-config,最后如下:

PERFTOOLS_CHECK_CMD = $(CC) $(CPPFLAGS) $(CFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS) `pkg-config --cflags libprofiler`

然后执行如下命令检查编译环境是否有问题:

# make run_dep_checks

其实,这里我们可以不用安装perftools,检查时发现有perftools方面的错误,但都是test里面的,因此可以忽略跳过。最后接着执行如下命令进行安装:

# make 
# make prefix=/usr/local/grpc
# make install prefix=/usr/local/grpc

2. gRPC示例

如下我们使用gRPC+protobuf实现一个回显功能。

1) 编写proto文件

如下我们实现一个回显服务器,编写echoserver.proto:

syntax = "proto3";

message EchoRequest {

        string request = 1;
}

message EchoResponse {
        string response = 1;
}

service EchoService {
        rpc echo(EchoRequest) returns (EchoResponse);
}

2) 自动生成protobuf及grpc代码

如下我们使用echoserver.proto自动生成grpc及protobuf代码:

# /usr/local/protobuf/bin/protoc --cpp_out=./ echoserver.proto
# /usr/local/protobuf/bin/protoc --grpc_out=./ --plugin=protoc-gen-grpc=/usr/local/grpc/bin/grpc_cpp_plugin echoserver.proto
# ls
echoserver.grpc.pb.cc  echoserver.grpc.pb.h  echoserver.pb.cc  echoserver.pb.h  echoserver.proto

这两个命令将会生成如下4个文件:echoserver.pb.c(h)、echoserver.grpc.pb.c(h)

3) 编写服务器代码

如下我们编写服务器代码grpc_echo_server.cpp:

#include <iostream>
#include <memory>
#include <string>
 
#include <grpc++/grpc++.h>
#include <grpc/grpc.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
 
#include "echoserver.grpc.pb.h"
 
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;

class EchoServiceImpl final : public EchoService::Service{
        //override echo 
        ::grpc::Status echo(::grpc::ServerContext* context, const ::EchoRequest* request, ::EchoResponse* response){
                std::string prefix("Hello ");
                response->set_response(prefix + request->request());
                return Status::OK;
        }
};
 
void RunServer(){
        std::string server_address("0.0.0.0:50051");
        EchoServiceImpl echoservice;

        ServerBuilder builder;
        builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
        builder.RegisterService(&echoservice);
        std::unique_ptr<Server> server(builder.BuildAndStart());

        std::cout << "Server listening on " << server_address << std::endl;

        server->Wait();
}

 
int main(int argc, char *argv[]){
        RunServer();

        return 0x0;
}

执行如下命令进行编译:

# export PKG_CONFIG_PATH=/usr/local/cares/lib/pkgconfig/:/usr/local/protobuf/lib/pkgconfig/:/usr/local/grpc/lib/pkgconfig/
# gcc -o grpc_echo_server ./echoserver.pb.cc ./echoserver.grpc.pb.cc ./grpc_echo_server.cpp \
-std=c++11 `pkg-config --cflags --libs grpc grpc++ protobuf libcares gpr` -lstdc++
# ls
echoserver.grpc.pb.cc  echoserver.grpc.pb.h  echoserver.pb.cc  echoserver.pb.h  echoserver.proto  grpc_echo_server  grpc_echo_server.cpp

4) 编写客户端代码

如下我们编写服务器代码grpc_echo_client.cpp:

#include <iostream>
#include <memory>
#include <string>
 
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
 
#include "echoserver.grpc.pb.h"
 
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
 
 
class EchoClient {
 public:
  explicit EchoClient(std::shared_ptr<Channel> channel)
      : stub_(EchoService::NewStub(channel)) {}
 
  std::string echo(const std::string& msg) {
 
    EchoRequest request;
    request.set_request(msg);
 
    EchoResponse reply;
   
    ClientContext context;
 
    CompletionQueue cq;
 
    Status status;
 
    std::unique_ptr<ClientAsyncResponseReader<EchoResponse> > rpc(
        stub_->Asyncecho(&context, request, &cq));
 
    rpc->Finish(&reply, &status, (void*)1);
    void* got_tag;
    bool ok = false;
  
    GPR_ASSERT(cq.Next(&got_tag, &ok));
 
   
    GPR_ASSERT(got_tag == (void*)1);
  
    GPR_ASSERT(ok);
 
    if (status.ok()) {
      return reply.response();
    } else {
      return "RPC failed";
    }
  }
 
 private:
 
  std::unique_ptr<EchoService::Stub> stub_;
};
 
int main(int argc, char *argv[]) {
  EchoClient client(grpc::CreateChannel(
      "localhost:50051", grpc::InsecureChannelCredentials()));
  std::string msg("world");
  std::string reply = client.echo(msg);  // The actual RPC call!
  std::cout << "client received: " << reply << std::endl;
 
  return 0;
} 

执行如下命令进行编译:

# export PKG_CONFIG_PATH=/usr/local/cares/lib/pkgconfig/:/usr/local/protobuf/lib/pkgconfig/:/usr/local/grpc/lib/pkgconfig/
# gcc -o grpc_echo_client ./echoserver.pb.cc ./echoserver.grpc.pb.cc ./grpc_echo_client.cpp \
-std=c++11 `pkg-config --cflags --libs grpc grpc++ protobuf libcares gpr` -lstdc++
# ls
echoserver.grpc.pb.cc  echoserver.pb.cc  echoserver.proto  grpc_echo_client.cpp  grpc_echo_server.cpp
echoserver.grpc.pb.h   echoserver.pb.h   grpc_echo_client  grpc_echo_server

5) 运行测试

服务端运行:

# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/:/usr/local/grpc/lib/:/usr/local/cares/lib/
# ./grpc_echo_server 
Server listening on 0.0.0.0:50051

客户端运行:

# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/:/usr/local/grpc/lib/:/usr/local/cares/lib/
# ./grpc_echo_client 
client received: Hello world



[参看]:

  1. gRPC GitHub

  2. gRPC官网

  3. grpc+protobuf 的C++ service 实例解析

  4. Protobuf github

  5. protobuf developers

  6. libcares库

  7. libunwind安装

  8. gperftools GitHUB