天天看點

grpc for c++使用案例

grpc for c++使用案例

來源:微信公衆号「程式設計學習基地」

文章目錄

      • proto檔案編輯
        • 生成C++代碼
        • 序列化接口
      • grpc server端
        • 命名空間
        • 重寫服務
        • 啟動服務
        • 完整代碼
      • grpc client端
        • 命名空間
        • 定義用戶端
        • 建立通道
        • 完整代碼

proto檔案編輯

syntax = "proto3";
package helloworld;

// 定義服務
service Greeter {
  // 定義服務函數
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc PlayStart (PlayStartReq) returns (PlayStartRes){}  
}

// 定義SayHello服務的請求結構
message HelloRequest {
  string name = 1;
  string test = 2;
}

// 定義SayHello服務的響應結構
message HelloReply {
  string message = 1;
}

// 定義播放請求結構
message PlayStartReq {
	uint32 sessionid  = 1;	 
	string eleStmName = 2;
}
// 定義播放響應結構
message PlayStartRes{
	int32  result 		= 1; 
	string extension  	= 2;
}
           

第二行:包的名字

package helloworld;

helloworld

第五行:grpc服務的名稱

service Greeter

Greeter

後面的請求響應結構,無論名字是大寫還是小寫,最終調用的時候都是小寫。

生成C++代碼

protoc --cpp_out=. helloworld.proto
           

生成C++代碼helloworld.pb.h和helloworld.pb.cc。

生成grpc代碼

protoc -I ./ --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` helloworld.proto
           

序列化接口

bool SerializeToString(string* output) const

序列化消息,将消息對象以string方式輸出。

bool ParseFromString(const string& data)

反序列化消息,解析給定的string為消息對象

bool SerializeToOstream(ostream* output) const

序列化消息,将消息對象寫入給定的c++ ostream中

bool ParseFromIstream(istream* input)

反序列化消息,從給定的c++ istream中解析出消息對象

grpc server端

命名空間

記住首先一定要開放命名空間

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::PlayStartRes;
           

重寫服務

定義服務端的類,繼承

proto

檔案定義的

grpc

服務

Greeter

,重寫

grpc

服務定義的方法

class GreeterServiceImpl : public Greeter::Service {
  //重寫 hello 方法
  virtual Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override {
    std::string prefix("Hello ");
    std::cout << "test:"<<request->test() << std::endl;
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
  //重寫 開流 方法
  virtual Status PlayStart(ServerContext* context, const PlayStartReq* request,PlayStartRes* reply) override {
    std::string prefix("Hello ");
    std::cout << "session id:" << request->sessionid() << std::endl;
    std::cout << "eleStmName:" << request->elestmname() << std::endl;
    reply->set_result(10);
    reply->set_extension("extension");
    return Status::OK;
  }
};
           

在重寫方法的時候可以通過

request

結構裡面的成員名函數擷取改成員,例如:

message PlayStartReq {
	uint32 sessionid  = 1;	 
	string eleStmName = 2;
}
           
const PlayStartReq* request;
std::cout << "session id:" << request->sessionid() << std::endl;
std::cout << "eleStmName:" << request->elestmname() << std::endl;
           

同時通過

reply

結構裡面的

set_

+

成員名

函數設定成員,例如:

message PlayStartRes{
	int32  result 		= 1; 
	string extension  	= 2;
}
           
PlayStartRes* reply;
reply->set_result(10);
reply->set_extension("extension");
           

啟動服務

std::string server_address("0.0.0.0:50051");
  /* 定義重寫的服務類 */
  GreeterServiceImpl service;

  /* 建立工廠類 */
  ServerBuilder builder;
  /* 監聽端口和位址 */
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 5000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
  /* 注冊服務 */
  builder.RegisterService(&service);
  /** 建立和啟動一個RPC伺服器*/
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  /* 進入服務事件循環 */
  server->Wait();
           

完整代碼

#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::PlayStartRes;

class GreeterServiceImpl : public Greeter::Service {
  //重寫 hello 方法
  virtual Status SayHello(ServerContext* context, const HelloRequest* request,HelloReply* reply) override {
    std::string prefix("Hello ");
    std::cout << "test:"<<request->test() << std::endl;
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
  //重寫 開流 方法
  virtual Status PlayStart(ServerContext* context, const PlayStartReq* request,PlayStartRes* reply) override {
    std::string prefix("Hello ");
    std::cout << "session id:" << request->sessionid() << std::endl;
    std::cout << "eleStmName:" << request->elestmname() << std::endl;
    reply->set_result(10);
    reply->set_extension("extension");
    return Status::OK;
  }
};

void RunServer() {
  std::string server_address("0.0.0.0:50051");
  /* 定義重寫的服務類 */
  GreeterServiceImpl service;

  /* 建立工廠類 */
  ServerBuilder builder;
  /* 監聽端口和位址 */
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIME_MS, 5000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
  builder.AddChannelArgument(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
  /* 注冊服務 */
  builder.RegisterService(&service);
  /** 建立和啟動一個RPC伺服器*/
  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 0;
}
           

grpc client端

命名空間

記住首先一定要開放命名空間

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::PlayStartRes;
           

定義用戶端

定義用戶端的類,實作兩個方法用來發送grpc請求以及接收grpc響應

class GreeterClient
{
public:
    GreeterClient(std::shared_ptr<Channel> channel)
        : stub_(Greeter::NewStub(channel)) {}
    std::string SayHello(const std::string &user)
    {
        HelloRequest request;
        request.set_name(user);
        request.set_test("test");
        HelloReply reply;
        ClientContext context;
        Status status = stub_->SayHello(&context, request, &reply);
        if (status.ok())
        {
            return reply.message();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message()<< std::endl;
            return "RPC failed";
        }
    }

    std::string Test(const std::string &user)
    {
        ClientContext context;
        PlayStartReq req;
        PlayStartRes res;
        req.set_sessionid(10);
        req.set_elestmname("BStar");

        Status status = stub_->PlayStart(&context, req, &res);

        if (status.ok())
        {
            std::cout << "result:" << res.result() << std::endl;
            return res.extension();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message()<< std::endl;
            return "RPC failed";
        }
    }
private:
    std::unique_ptr<Greeter::Stub> stub_;
};
           

主要是定義兩個方法用來發送grpc請求以及接收grpc響應

同樣在實作方法的時候可以通過

request

結構裡面的

set_

+

成員名

函數設定成員

message PlayStartReq {
	uint32 sessionid  = 1;	 
	string eleStmName = 2;
}
           
PlayStartReq req;
req.set_sessionid(10);
req.set_elestmname("BStar");
           

同時通過

request

結構裡面的成員名函數擷取改成員

message PlayStartRes{
	int32  result 		= 1; 
	string extension  	= 2;
}
           
PlayStartRes res;
std::cout << "result:" << res.result() << std::endl;
           

建立通道

定義用戶端的時候初始化通道

//定義服務端口和位址
  std::string target_str = "localhost:50051";
  //建立通道
  GreeterClient greeter(grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
  std::string user("world");
  //std::string reply = greeter.SayHello(user);
  //發送請求
  std::string reply = greeter.Test(user);
  std::cout << "Greeter received: " << reply << std::endl;
           

完整代碼

#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/grpcpp.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::PlayStartReq;
using helloworld::PlayStartRes;

class GreeterClient
{
public:
    GreeterClient(std::shared_ptr<Channel> channel)
        : stub_(Greeter::NewStub(channel)) {}

    // Assembles the client's payload, sends it and presents the response back
    // from the server.
    std::string SayHello(const std::string &user)
    {

        HelloRequest request;
        request.set_name(user);
        request.set_test("Greeter test");

        HelloReply reply;
        ClientContext context;
        Status status = stub_->SayHello(&context, request, &reply);

        if (status.ok())
        {
            return reply.message();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message()
                      << std::endl;
            return "RPC failed";
        }
    }
    std::string Test(const std::string &user)
    {
        ClientContext context;
        PlayStartReq req;
        PlayStartRes res;
        req.set_sessionid(10);
        req.set_elestmname(user);

        Status status = stub_->PlayStart(&context, req, &res);

        if (status.ok())
        {
            std::cout << "result:" << res.result() << std::endl;
            return res.extension();
        }
        else
        {
            std::cout << status.error_code() << ": " << status.error_message() << std::endl;
            return "RPC failed";
        }
    }

private:
    std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char **argv)
{
    //定義服務端口和位址
    std::string target_str = "localhost:50051";
    std::string reply;
    //建立通道
    GreeterClient greeter(grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
    std::string user("world");
    reply = greeter.SayHello(user);
    std::cout << "Greeter received: " << reply << std::endl;
    //發送請求
    reply = greeter.Test(user);
    std::cout << "Greeter received: " << reply << std::endl;

    return 0;
}
           

繼續閱讀