使用Protobuf和gRPC:用C++构建高效的跨平台通信系统

文摘   2024-12-15 09:03   浙江  

Protocol Buffers (俗称Protobuf)和 gRPC 是谷歌开源的一对黄金搭档。它俩一个负责数据序列化,一个负责远程调用,配合起来贼好使。要是你还在用JSON来传数据,不妨试试这套组合拳,性能能提升好几倍。


配置开发环境

搭建环境是第一道坎。老规矩,得先装好这些东西:


  • Protobuf编译器
     (protoc)
  • gRPC C++库
  • CMake(用来构建项目)


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倍,体积还小不少。原因在于它用了 二进制格式 和一些骚操作:


  • 数字用 变长编码
  • 字段用数字ID标识
  • 对齐优化
  • 跳过默认值


安全性问题不能忽视

生产环境别用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 {

// 具体实现略...

};

这套组合拳用熟了,写微服务不要太爽。性能好、跨语言、还自带代码生成,我都用了好几个项目了。不过新手上手可能有点懵,建议先拿个小项目试试水。



椰子树的秘密
优质内容创作者
 最新文章