關於GRPC的講解


gRPC服務發現&負載均衡

https://segmentfault.com/a/1190000008672912?utm_source=tag-newest

GRPC編程指南

gRPC 介紹

  gRPC 是谷歌開源的高性能 RPC 框架。RPC 也即遠程方法調用,對於 RPC client 來說,它可以調用遠程 server 上的某個方法,看起來就像是在調用本地方法一樣。區別就在於,通過 RPC 調用遠程方法時,數據經過序列化之后會通過網絡發送給遠程 server,遠程 server 執行方法之后,同樣會將返回結果序列化之后發送回 client。在分布式系統中,gRPC 可以用來解耦程序的邏輯,不同組件之間通過 gRPC 進行通信。
  gRPC 使用 Protobuf 作為它的數據序列化的工具,Protobuf 會將數據序列化成二進制的數據流。與 JSON 這類文本形式的數據相比,二進制數據顯得更加緊湊和便於解析,在網絡傳輸中,二進制數據由於體積更小,傳輸也更快。另一方面,gRPC 也是跨多種編程語言的,譬如說,一個 Java 的 client 可以與一個 C++ 的 server 通信。

 

在 Linux 安裝 gRPC

  在 Ubuntu 16.04 中,通過下面的步驟就可以安裝好 gRPC 和 Protobuf。

1
2
3
4
5
6
7
8
9
10
11
sudo apt-get install build-essential autoconf libtool libgflags-dev libgtest-dev clang libc++-dev pkg-config unzip
git clone -b $(curl -L http://grpc.io/release) https://github.com/grpc/grpc
cd grpc
git submodule update --init
make
sudo make install
cd third_party/protobuf
sudo ./autogen.sh
sudo ./configure
make
sudo make install

 

構建服務端程序

  在創建 gRPC 服務(service)之前,首先需要提供這個服務的接口。Protobuf 除了作為數據序列化工具之外,還可以用來為服務定義接口。例如,下面我們為 Company 服務定義接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
syntax = "proto3";
package company;
 
service Company {
rpc AddEmployee(Employee) returns (EmployeeID) {} // 提交員工信息
rpc ListEmployees(AgeRange) returns (stream Employee) {} // 查詢員工信息
}
 
message Employee {
string name = 1;
int32 age = 2;
}
 
message EmployeeID {
int32 id = 1;
}
 
message AgeRange {
int32 low = 1;
int32 high = 2;
}

 

  我們為 Company 服務定義了兩個方法,AddEmployee()用於提交員工信息,而ListEmployees()則用於根據年齡查詢員工信息。注意到ListEmployees()方法的返回值類型是stream Employee,這表示這個方法會返回多個Employee消息。
  執行下面的命令可以自動生成 ProtoBuf 編解碼的代碼,以及與 Company 服務相關的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ tree
├── cpp
└── protos
└── company.proto
$ protoc -I protos --grpc_out=cpp --plugin=protoc-gen-grpc=` which grpc_cpp_plugin` protos/company.proto
$ protoc -I protos --cpp_out=cpp protos/company.proto
$ tree
├── cpp
│   ├── company.grpc.pb.cc
│   ├── company.grpc.pb.h
│   ├── company.pb.cc
│   └── company.pb.h
└── protos
└── company.proto

 

  在company.grpc.ph.h文件里面,已經定義好了Company::Service這個抽象基類,我們可以繼承這個基類,並提供方法的具體實現。下面我們創建一個company_server.cc文件,並定義CompanyImpl這個具體類,同時提供方法的具體實現。值得注意的是,我們需要保證CompanyImpl提供的方法都是線程安全的,因為這些方法允許被多個 client 同時調用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// file company_server.cc
#include "company.grpc.pb.h"
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <grpc/grpc.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc++/security/server_credentials.h>
 
using company::Company;
using company::AgeRange;
using company::Employee;
using company::EmployeeID;
 
class CompanyImpl final : public Company::Service
{
public:
CompanyImpl()
: nextID_( 0) { }
 
grpc:: Status AddEmployee(grpc::ServerContext *context, const Employee *request,
EmployeeID *response) override
{
std::lock_guard<std::mutex> guard(mtx_);
employees_[nextID_] = *request;
response->set_id(nextID_);
++nextID_;
return grpc::Status::OK;
}
 
grpc:: Status ListEmployees(grpc::ServerContext *context, const AgeRange *request,
grpc::ServerWriter<Employee> *writer) override
{
auto low = request->low();
auto high = request->high();
std::lock_guard<std::mutex> guard(mtx_);
for (auto &entry : employees_)
{
auto employee = entry.second;
if (employee.age() >= low && employee.age() <= high)
{
writer->Write(employee); // 調用 Write 寫入一個 Employee 消息給client
}
}
return grpc::Status::OK;
}
 
private:
int32_t nextID_;
std::mutex mtx_;
std::unordered_map<int32_t, Employee> employees_;
};
 
int main(int argc, char *argv[])
{
std::string addr = "0.0.0.0:5000";
CompanyImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort(addr, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
auto server = builder.BuildAndStart();
std::cout << "Server listening on " << addr << std::endl;
server->Wait();
 
return 0;
}

 

構建客戶端程序

  client 的代碼相對簡單很多,為了讓 client 可以調用 server 提供的方法,首先需要創建一個 stub:

1
2
3
// 第二個參數表示不開啟 SSL
auto channel = grpc::CreateChannel("localhost:5000", grpc::InsecureChannelCredentials());
auto stub = Company::NewStub(channel);

 

  client 通過這個 stub 就可以直接調用 server 提供的方法了。下面我們創建一個company_client.cc文件,用來對 server 進行測試:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "company.grpc.pb.h"
#include <iostream>
#include <memory>
#include <string>
#include <grpc/grpc.h>
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
 
using company::Company;
using company::Employee;
using company::EmployeeID;
using company::AgeRange;
 
class CompanyClient
{
public:
CompanyClient( std::shared_ptr<grpc::Channel> channel)
: stub_(Company::NewStub(channel)) { }
 
void AddEmployee(const std::string &name, int32_t age)
{
Employee employee;
employee.set_name(name);
employee.set_age(age);
 
EmployeeID id;
grpc::ClientContext context;
stub_->AddEmployee(&context, employee, &id);
std::cout << "AddEmployee() - new id: " << id.id() << std::endl;
}
 
void ListEmployeesByAge(int32_t low, int32_t high)
{
AgeRange range;
range.set_low(low);
range.set_high(high);
grpc::ClientContext context;
auto reader = stub_->ListEmployees(&context, range);
Employee employee;
while (reader->Read(&employee))
{
std::cout << "Employee: name = " << employee.name() << ", age = " << employee.age() << std::endl;
}
}
 
private:
std::unique_ptr<Company::Stub> stub_;
};
 
int main(int argc, char *argv[])
{
auto channel = grpc::CreateChannel("localhost:5000", grpc::InsecureChannelCredentials());
CompanyClient client(channel);
 
client.AddEmployee( "hello", 10);
client.AddEmployee( "world", 20);
client.ListEmployeesByAge( 0, 100);
return 0;
}

 


  編譯好 server 和 client 程序之后就可以運行了:

1
2
$ clang++ -std=c++11 -o server -lgrpc++ -lprotobuf -lpthread -lgrpc++_reflection company.pb.cc company.grpc.pb.cc company_server.cc
$ clang++ -std=c++11 -o client -lgrpc++ -lprotobuf -lpthread -lgrpc++_reflection company.pb.cc company.grpc.pb.cc company_client.cc

 

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM