gRPC

gRPC

RPC

RPC(Remote Procedure Call Protocol)–遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通信程序之間攜帶信息數據。在OSI網絡通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。

RPC採用客戶機/服務器模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,然後等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息的到達爲止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答覆信息,然後等待下一個調用信息,最後,客戶端調用進程接收答覆信息,獲得進程結果,然後調用執行繼續進行。

rpc原理圖

簡介

gRPC 一開始由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用(RPC)系統。

gRPC 是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。

gRPC支持語言

gRPC調用模型

gRPC 默認使用 protocol buffers,這是 Google 開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如 JSON)。

編譯

$ git clone https://github.com/grpc/grpc.git
$ cd grpc
$ git submodule update --init
$ make
$ sudo make install

TestCase

下面以c++爲測試語言

proto定義

// Login.proto
syntax="proto3";

package rs;

message Response{
    int32 code = 1;
    string errmsg = 2;
}

message LoginRequest{
    string username = 1;
    string password = 2;
}

message LoginResponse{
    int32 id = 1;
    string username = 2;
    string token = 3;
}

message LogoutRequest{
    int32 id = 1;
    string username = 2;
    string token = 3;
}

service LoginService{
    rpc Login(LoginRequest) returns (LoginResponse) {}
    rpc Logout(LogoutRequest) returns (Response) {}
}
$ protoc -Iproto --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` rs/Login.proto --grpc_out=cpp

$ protoc -Iproto rs/Login.proto --cpp_out=cpp

服務端代碼

#include <grpcpp/grpcpp.h>
#include "rs/Login.grpc.pb.h"

using namespace rs;
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;

using rs::Response;
using rs::LoginRequest;
using rs::LoginResponse;
using rs::LogoutRequest;

class LoginServiceImpl : public LoginService::Service{
public:
    LoginServiceImpl() {}
    ~LoginServiceImpl() {}
    virtual ::grpc::Status Login(ServerContext* context, const LoginRequest* request, LoginResponse* response){
        response->set_id(1);
        response->set_username(request->username());
        response->set_token("ABCDEFGHIJKLMNOPQRSTUVWXYZ123456");
        return Status::OK;
    }

    virtual ::grpc::Status Logout(ServerContext* context, const LogoutRequest* request, Response* response){
        response->set_code(200);
        response->set_errmsg("OK");
        return Status::OK;      
    }
};

void RunServer() {
    std::string server_address("0.0.0.0:12345");
    LoginServiceImpl 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++ -g -Wall -std=c++11 server.cpp rs/Login.grpc.pb.cc rs/Login.pb.cc -I. -o server -lprotobuf -lgrpc++

客戶端代碼

#include <grpcpp/grpcpp.h>
#include "rs/Login.grpc.pb.h"

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;

using namespace rs;

int main(int argc, char* argv[]){
    std::string srvaddr = "127.0.0.1:12345";
    std::shared_ptr<Channel> channel = grpc::CreateChannel(srvaddr, grpc::InsecureChannelCredentials());

    std::unique_ptr<LoginService::Stub> stub = LoginService::NewStub(channel);

    ClientContext ctx;
    LoginRequest req;
    req.set_username("hw");
    req.set_password("admin123");
    LoginResponse res;
    Status status = stub->Login(&ctx, req, &res);
    if (status.ok()){
        req.PrintDebugString();
        res.PrintDebugString();
    }else{
        printf("{errcode:%d,errmsg:%s,detail:%s}", status.error_code(),
            status.error_message().c_str(), status.error_details().c_str());
    }

    LogoutRequest reqLogout;
    reqLogout.set_id(res.id());
    reqLogout.set_username(res.username());
    reqLogout.set_token(res.token());
    Response resLogout;
    ClientContext ctx2;
    status = stub->Logout(&ctx2, reqLogout, &resLogout);
    if (status.ok()){
        resLogout.PrintDebugString();
    }else{
        printf("{errcode:%d,errmsg:%s,detail:%s}", status.error_code(),
            status.error_message().c_str(), status.error_details().c_str());       
    }

    return 0;
}
$ g++ -g -Wall -std=c++11 client.cpp rs/Login.grpc.pb.cc rs/Login.pb.cc -I. -o client -lprotobuf -lgrpc++

參考資料

gRPC官方文檔:https://grpc.io/docs/
gPRC中文文檔:http://doc.oschina.net/grpc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章