Protocol Buffers (俗称Protobuf)和 gRPC 是谷歌开源的一对黄金搭档。它俩一个负责数据序列化,一个负责远程调用,配合起来贼好使。要是你还在用JSON来传数据,不妨试试这套组合拳,性能能提升好几倍。
搭建环境是第一道坎。老规矩,得先装好这些东西:
Windows上装这些可能有点麻烦,建议用vcpkg:
vcpkg install protobuf:x64-windows
vcpkg install grpc:x64-windows
温馨提示:别忘了把vcpkg加到系统环境变量,不然CMake找不着。
写代码前,得先定义数据结构。创建一个user.proto
文件:
syntax = “proto3”;
package userservice;
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
service UserService {
rpc GetUser (GetUserRequest) returns (User) {}
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse) {}
}
message GetUserRequest {
int32 user_id = 1;
}
message ListUsersRequest {
int32 page_size = 1;
}
message ListUsersResponse {
repeated User users = 1;
}
用 protoc 把这个.proto
文件转成C++代码:
protoc -I=. --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin user.proto
服务端代码得有点意思,咱们整一个内存数据库:
#include <grpcpp/grpcpp.h>
#include “user.grpc.pb.h”
#include <unordered_map>
class UserServiceImpl final : public userservice::UserService::Service {
private:
std::unordered_map<int32_t, userservice::User> users_;
public:
grpc::Status GetUser(grpc::ServerContext* context,
const userservice::GetUserRequest* request,
userservice::User* response) override {
auto it = users_.find(request->user_id());
if (it == users_.end()) {
return grpc::Status(grpc::StatusCode::NOT_FOUND, “用户不存在啊老铁”);
}
*response = it->second;
return grpc::Status::OK;
}
};
温馨提示: gRPC 的错误处理特别优雅,咱们可以用 Status 来返回各种状态码和错误信息。
客户端代码也很简单,就像调用本地函数一样:
#include <grpcpp/grpcpp.h>
#include “user.grpc.pb.h”
int main() {
auto channel = grpc::CreateChannel(“localhost:50051”,
grpc::InsecureChannelCredentials());
auto stub = userservice::UserService::NewStub(channel);
userservice::GetUserRequest request;
request.set_user_id(123);
userservice::User user;
grpc::ClientContext context;
auto status = stub->GetUser(&context, request, &user);
if (status.ok()) {
std::cout << “找到用户:” << user.name() << std::endl;
}
}
Protobuf 的序列化速度比JSON快3-10倍,体积还小不少。原因在于它用了 二进制格式 和一些骚操作:
生产环境别用InsecureChannelCredentials()
,太危险了。得上 SSL/TLS :
auto creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
auto channel = grpc::CreateChannel(“localhost:50051”, creds);
实际项目里记得加上错误重试、超时控制、负载均衡这些,不然线上出了问题哭都来不及。
开发时可以开启 gRPC的调试日志 :
gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
还可以用 拦截器 (Interceptor)来记录每次调用的耗时:
class TimingInterceptor : public grpc::experimental::Interceptor {
// 具体实现略...
};
这套组合拳用熟了,写微服务不要太爽。性能好、跨语言、还自带代码生成,我都用了好几个项目了。不过新手上手可能有点懵,建议先拿个小项目试试水。