文章目录
下方实例演示一个客户端和三个服务器进行通信的分布式grpc服务
1、定义服务(proto文件)
calculator.proto:
syntax = "proto3";
service Calculator {
rpc Add (AddRequest) returns (AddResponse);
}
message AddRequest {
int32 a = 1;
int32 b = 2;
}
message AddResponse {
int32 result = 1;
}
2、生成grpc代码
使用 protoc 工具生成 C++ 的 gRPC 代码:
protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` calculator.proto
这将生成 calculator.pb.h, calculator.pb.cc, calculator.grpc.pb.h, 和 calculator.grpc.pb.cc 文件。
3、实现服务端
创建多个服务器实例,每个服务器监听不同的 IP 和端口。
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "calculator.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;
class CalculatorServiceImpl final : public Calculator::Service {
Status Add(ServerContext* context, const AddRequest* request, AddResponse* reply) override {
int sum = request->a() + request->b();
reply->set_result(sum);
return Status::OK;
}
};
void RunServer(const std::string& server_address) {
CalculatorServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
std::string server_address = "0.0.0.0:50051"; // 可以通过argv传入不同的端口
if (argc > 1) {
server_address = std::string(argv[1]);
}
RunServer(server_address);
return 0;
}
编译服务器端代码并在不同端口上启动三个实例:
4、实现客户端
在客户端中,设置多个服务器端的 IP 和端口,并使用简单的轮询机制或其他负载均衡策略来分发请求。
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include "calculator.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using calculator::Calculator;
using calculator::AddRequest;
using calculator::AddResponse;
class CalculatorClient {
public:
CalculatorClient(std::shared_ptr<Channel> channel) : stub_(Calculator::NewStub(channel)) {}
int Add(int a, int b) {
AddRequest request;
request.set_a(a);
request.set_b(b);
AddResponse response;
ClientContext context;
Status status = stub_->Add(&context, request, &response);
if (status.ok()) {
return response.result();
} else {
std::cout << "RPC failed" << std::endl;
return -1;
}
}
private:
std::unique_ptr<Calculator::Stub> stub_;
};
int main() {
// 假设三个服务器的 IP 地址和端口如下
std::vector<std::string> server_addresses = {
"192.168.1.101:50051",
"192.168.1.102:50052",
"192.168.1.103:50053"
};
int next_server = 0;
// 轮询机制来选择服务器
for (int i = 0; i < 3; ++i) {
// 创建到当前服务器的通道
CalculatorClient client(grpc::CreateChannel(server_addresses[next_server], grpc::InsecureChannelCredentials()));
// 发起 Add 请求
int result = client.Add(3, 5);
std::cout << "Add result from server " << server_addresses[next_server] << ": " << result << std::endl;
// 轮询下一个服务器
next_server = (next_server + 1) % server_addresses.size();
}
return 0;
}
服务器地址配置:在 server_addresses 向量中,列出了每个服务器的 IP 地址和端口。客户端将根据这个列表逐一连接到不同的服务器。
轮询机制:通过 next_server 的索引,客户端会轮流选择下一个服务器地址,从而将请求分发到不同的服务器实例。
5、编译和运行客户端
编译客户端代码:
g++ -std=c++11 client.cpp calculator.pb.cc calculator.grpc.pb.cc -o client -lgrpc++ -lprotobuf -lpthread
运行客户端:
./client
在这个实现中:
服务器端通过在不同的端口上启动三个实例来实现分布式。
客户端通过一个简单的轮询机制来选择服务器,依次发送请求到不同的服务器。
你可以根据需求扩展客户端的负载均衡策略,比如使用随机选择、哈希分片,或集成更复杂的服务发现和负载均衡工具。
grpc实现分布式系统的多种方式,具体选择取决于系统的需求和架构设计。
1、客户端负载均衡(一般用于选择哪一个实例)
客户端负载均衡是指在客户端进行服务实例的选择和请求的分发。客户端可以配置多个服务器地址,通过轮询、随机、哈希分片等策略,将请求分发到不同的服务器实例。
实现方式:
手动配置:客户端显式地配置多个服务器地址并自行实现负载均衡策略(如前面示例中的轮询)。
gRPC 内置负载均衡:gRPC 提供了内置的负载均衡策略,如 round_robin(轮询)和 pick_first(优选第一个可用服务器)。
DNS 负载均衡:使用 DNS 解析来获取多个服务器地址,gRPC 客户端会根据解析结果选择服务器。
2、服务发现(一般用于找到服务器实例)
服务发现系统帮助客户端动态找到可用的服务实例,而不需要在客户端硬编码服务器地址。服务发现通常与负载均衡结合使用。
实现方式:
外部服务发现工具:使用工具如 Consul、etcd、Zookeeper 等进行服务注册和发现,客户端通过查询这些服务发现系统来获取可用的服务器地址。
DNS-SRV 服务发现:一些服务发现工具支持 DNS-SRV 记录,客户端通过查询特定的 DNS-SRV 记录来获取服务实例列表。
3、代理和网关
使用代理或网关集中处理客户端请求的负载均衡、路由和服务发现。这种方式将负载均衡从客户端移到代理层,使客户端只需与单个网关或代理通信。
实现方式:
Envoy Proxy:Envoy 是一种流行的 L7 代理,可以用作 gRPC 的服务网格,提供负载均衡、服务发现、熔断等功能。
gRPC Gateway:gRPC Gateway 可以作为反向代理,接收 HTTP/1.1 请求并转发给 gRPC 服务,还可以与 Envoy 结合使用。
Nginx:Nginx 也可以配置为 gRPC 的负载均衡器,处理客户端请求的分发。
4. 服务网格(Service Mesh)
服务网格是一种用于微服务架构的基础设施层,管理服务之间的通信,通常提供动态服务发现、负载均衡、流量管理、安全性等功能。
实现方式:
Istio:Istio 是一种流行的服务网格实现,可以无缝地集成 gRPC 服务,提供流量管理、负载均衡、认证授权等功能。
Linkerd:Linkerd 是另一个服务网格实现,也支持 gRPC,主要用于提高系统的可观测性、弹性和安全性。