C++ grpc简单实例解析,源码分析

proto 文件

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

编译 proto 文件

protoc --cpp_out=. helloworld.proto
protoc --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin helloworld.proto

获得四个文件
在这里插入图片描述

服务端代码

#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>

#include "helloworld.grpc.pb.h"

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

// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloReply* reply) override {
    std::string prefix("wanjun Hello ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }
};

void RunServer() {
  std::string server_address("0.0.0.0:50051");
  GreeterServiceImpl service;

  ServerBuilder builder;
  // Listen on the given address without any authentication mechanism.
  // 监听给定的地址
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  // Register "service" as the instance through which we'll communicate with
  // clients. In this case it corresponds to an *synchronous* service.
  builder.RegisterService(&service);
  // Finally assemble the server.
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;

  // Wait for the server to shutdown. Note that some other thread must be
  // responsible for shutting down the server for this call to ever return.
  server->Wait();
}

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

  return 0;
}

编译指令

g++ -std=c++11 *.cc -o server -pthread -lprotobuf -lgrpc++

编译成功后便可执行

详细解析

class GreeterServiceImpl final : public Greeter::Service 

我们的Greeter服务器自动生成了一个嵌套类Service,用于继承并实现我们的虚函数。
在server.cc文件中,我们构造了Service的子类对象GreeterServiceImpl用于实现我们特定的方法。

Greeter::Service 源码

  class Service : public ::grpc::Service {
   public:
    Service();
    virtual ~Service();
    // Sends a greeting
    virtual ::grpc::Status SayHello(::grpc::ServerContext* context, const ::helloworld::HelloRequest* request, ::helloworld::HelloReply* response);
  };

由此可见只有一个虚函数SayHello
该虚函数在 .cc 文件中的实现为

::grpc::Status Greeter::Service::SayHello(::grpc::ServerContext* context, const ::helloworld::HelloRequest* request, ::helloworld::HelloReply* response) {
  (void) context;
  (void) request;
  (void) response;
  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}

并没有任何任何实现,只是给虚函数提供了一个基类方法。
SayHello 方法的参数如下
grpc::ServerContext context 上下文
helloworld::HelloRequest
request 传入的参数(请求)
helloworld::HelloReply* response 传出的结果(回复)
在 .proto 文件中我们已经定义了 HelloRequest & HelloReply 两个Message

  Status SayHello(ServerContext* context, const HelloRequest* request,
                  HelloReply* reply) override {
    std::string prefix("wanjun Hello ");
    reply->set_message(prefix + request->name());
    return Status::OK;
  }

override 表示覆盖基类中的虚函数,在实现中为获取了每个request的 name field ,并且添加了一个前缀作为reply返回。

RunServer() 函数

void RunServer() {
  std::string server_address("0.0.0.0:50051");
  GreeterServiceImpl service;

  ServerBuilder builder;
  // Listen on the given address without any authentication mechanism.
  // 监听给定的地址
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  // Register "service" as the instance through which we'll communicate with
  // clients. In this case it corresponds to an *synchronous* service.
  builder.RegisterService(&service);
  // Finally assemble the server.
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;

  // Wait for the server to shutdown. Note that some other thread must be
  // responsible for shutting down the server for this call to ever return.
  server->Wait();
}

ServerBuilder 作为最重要的一个类
/// A builder class for the creation and startup of \a grpc::Server instances.
一个builder用于创建和开启一个grpc服务器实例

ServerBuilder& AddListeningPort(const grpc::string& addr_uri,
                                  std::shared_ptr<ServerCredentials> creds,
                                  int* selected_port = nullptr);

为一个grpc::Server绑定一个端口。

ServerBuilder& RegisterService(Service* service);

注册一个服务,函数调用并不会获得service的所有权。

virtual std::unique_ptr<Server> BuildAndStart();

返回一个运行的服务器,已经准备好了去处理函数调用。
智能指针 std::unique_ptr 会自动清理内存。

Server

  /// Block until the server shuts down.
  /// \warning The server must be either shutting down or some other thread must
  /// call \a Shutdown for this function to ever return.
  void Wait() override;

阻塞等待服务器关闭

Client 客户端

#include <string>
#include <iostream>
#include <memory>
#include <grpcpp/grpcpp.h>
#include "helloworld.grpc.pb.h"
using grpc::ClientContext;
using grpc::Channel;
using grpc::Status;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::Greeter;

// static method : Greeter::NewStub
class wjClient{
public:
    wjClient(std::shared_ptr<Channel> channel)
        :stub_(Greeter::NewStub(channel)){
    }

    std::string SayHello(std::string name){
        ClientContext context;
        HelloReply reply;
        HelloRequest request;
        request.set_name(name);

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

        if(status.ok()){
            return reply.message();
        }else{
            return "failure";
        }
    }

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

int main(int argc,char *argv[]){
    auto channel = grpc::CreateChannel("0.0.0.0:5001",grpc::InsecureChannelCredentials());
    wjClient client(channel);
    // block until get result from RPC server
    std::string result = client.SayHello("wanjun");
    printf("get result [%s]\n",result.c_str());
    return 0;
}

解析
在Greeter 类中 StubInterface 类实现了提供给 Client 客户端使用的接口

class Stub final : public StubInterface

表示 Stub 继承自 StubInterface 并且不可被再继承,在我们的客户端代码中当做 wjClient 的私有变量 stub_ 使用。
stub_ 的赋值使用 Greeter::NewStub() 类静态方法获取,后续调用 SayHello() 完成对 RPC server 远程服务器的调用。

Channel 通道

每个 Client 都需要 Channel 来实现对远程服务器的调用,使用 grpc::CreateChannel() 函数来创建一个通信管道。

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
gRPC是一款开源的高性能远程过程调用(RPC)框架,由Google开发并开源。它基于HTTP/2和Protocol Buffers来实现跨平台、多语言的远程方法调用。grpc-go是gRPC的Go语言实现。 首先,我们来分析grpc-go的源码结构。grpc的核心代码位于grpc-go目录下,包括server、client、metadata等模块的代码实现。其中,server目录下的代码主要负责服务端的初始化、启动和处理请求;client目录下的代码则主要负责客户端的连接和发送请求;metadata目录下的代码保存了gRPC使用的元数据信息。 接着,我们来看一下grpc-go的基本工作流程。在服务端,首先要创建一个grpc.Server对象,然后通过调用Server的RegisterService方法注册一个或多个服务;然后通过调用Server的Serve方法启动服务。在客户端,首先要建立与服务端的连接,通过调用grpc.Dial方法创建一个grpc.ClientConn对象;然后通过该对象创建一个或多个服务的Client对象,最后通过Client对象调用远程方法。 grpc-go的底层代码主要依赖于Go语言的标准库和一些第三方库。其中,标准库主要包括net、http、io等模块;第三方库主要包括golang/protobuf、google.golang.org/grpc等。grpc-go通过protobuf编译器生成的代码来对消息进行序列化和反序列化,利用HTTP/2协议的多路复用特性来提高通信效率。 grpc-go的源码解析还涉及一些高级特性,如流式RPC、拦截器、错误处理等。流式RPC可以实现客户端和服务端之间的双向流式通信,通过使用流来传输大量的数据。拦截器可以用于对请求和响应进行预处理和后处理,对于日志记录、认证、鉴权等方面非常有用。错误处理可以帮助程序员更好地处理可能发生的异常情况,提高代码的可靠性。 总而言之,grpc-go的源码解析涉及了很多基础知识和高级特性,需要深入理解和掌握。通过对grpc-go源码分析,我们可以更好地理解它的工作原理,从而能够更好地使用和扩展该框架。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值