Skip to content

gRPC C++ — RPC 框架

gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 和 Protocol Buffers,是微服务通信的标准选择。

安装

bash
# vcpkg
vcpkg install grpc protobuf

# CMake
find_package(gRPC REQUIRED)
find_package(Protobuf REQUIRED)
target_link_libraries(myapp PRIVATE gRPC::grpc++ protobuf::libprotobuf)

定义服务(Proto)

protobuf
// user.proto
syntax = "proto3";
package user;

service UserService {
    rpc GetUser (GetUserRequest) returns (UserResponse);
    rpc CreateUser (CreateUserRequest) returns (UserResponse);
    rpc ListUsers (ListUsersRequest) returns (stream UserResponse);  // 服务端流
    rpc Chat (stream ChatMessage) returns (stream ChatMessage);      // 双向流
}

message GetUserRequest { int32 id = 1; }
message CreateUserRequest {
    string name = 1;
    int32 age = 2;
    string email = 3;
}
message UserResponse {
    int32 id = 1;
    string name = 2;
    int32 age = 3;
    string email = 4;
}
message ListUsersRequest { int32 page = 1; int32 limit = 2; }
message ChatMessage { string user = 1; string text = 2; }
bash
# 生成 C++ 代码
protoc --cpp_out=. --grpc_out=. \
       --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` \
       user.proto
# 生成:user.pb.h user.pb.cc user.grpc.pb.h user.grpc.pb.cc

服务端实现

cpp
#include <grpcpp/grpcpp.h>
#include "user.grpc.pb.h"

class UserServiceImpl final : public user::UserService::Service {
public:
    // 一元 RPC
    grpc::Status GetUser(grpc::ServerContext* ctx,
                         const user::GetUserRequest* req,
                         user::UserResponse* resp) override {
        int id = req->id();
        // 查询数据库...
        resp->set_id(id);
        resp->set_name("Alice");
        resp->set_age(30);
        resp->set_email("alice@example.com");
        return grpc::Status::OK;
    }

    // 服务端流式 RPC
    grpc::Status ListUsers(grpc::ServerContext* ctx,
                           const user::ListUsersRequest* req,
                           grpc::ServerWriter<user::UserResponse>* writer) override {
        for (int i = 0; i < req->limit(); ++i) {
            user::UserResponse u;
            u.set_id(i + 1);
            u.set_name("User " + std::to_string(i + 1));
            writer->Write(u);  // 流式写入
        }
        return grpc::Status::OK;
    }

    // 双向流式 RPC
    grpc::Status Chat(grpc::ServerContext* ctx,
                      grpc::ServerReaderWriter<user::ChatMessage,
                                               user::ChatMessage>* stream) override {
        user::ChatMessage msg;
        while (stream->Read(&msg)) {
            user::ChatMessage reply;
            reply.set_user("server");
            reply.set_text("Echo: " + msg.text());
            stream->Write(reply);
        }
        return grpc::Status::OK;
    }
};

void RunServer() {
    std::string addr = "0.0.0.0:50051";
    UserServiceImpl service;

    grpc::ServerBuilder builder;
    builder.AddListeningPort(addr, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);

    // 性能调优
    builder.SetMaxReceiveMessageSize(16 * 1024 * 1024);  // 16MB
    builder.SetMaxSendMessageSize(16 * 1024 * 1024);

    auto server = builder.BuildAndStart();
    std::cout << "gRPC 服务器启动: " << addr << "\n";
    server->Wait();
}

客户端

cpp
#include <grpcpp/grpcpp.h>
#include "user.grpc.pb.h"

class UserClient {
    std::unique_ptr<user::UserService::Stub> stub_;

public:
    explicit UserClient(std::shared_ptr<grpc::Channel> channel)
        : stub_(user::UserService::NewStub(channel)) {}

    // 一元调用
    std::optional<user::UserResponse> GetUser(int id) {
        user::GetUserRequest req;
        req.set_id(id);

        user::UserResponse resp;
        grpc::ClientContext ctx;
        ctx.set_deadline(std::chrono::system_clock::now() +
                         std::chrono::seconds(5));  // 超时

        auto status = stub_->GetUser(&ctx, req, &resp);
        if (status.ok()) return resp;

        std::cerr << "RPC 失败: " << status.error_message() << "\n";
        return std::nullopt;
    }

    // 服务端流
    void ListUsers(int limit) {
        user::ListUsersRequest req;
        req.set_limit(limit);

        grpc::ClientContext ctx;
        auto reader = stub_->ListUsers(&ctx, req);

        user::UserResponse u;
        while (reader->Read(&u)) {
            std::cout << u.id() << ": " << u.name() << "\n";
        }

        auto status = reader->Finish();
        if (!status.ok()) std::cerr << "流错误: " << status.error_message() << "\n";
    }
};

int main() {
    auto channel = grpc::CreateChannel("localhost:50051",
                                       grpc::InsecureChannelCredentials());
    UserClient client(channel);

    if (auto user = client.GetUser(1)) {
        std::cout << "用户: " << user->name() << "\n";
    }

    client.ListUsers(10);
}

TLS 安全连接

cpp
// 服务端 TLS
grpc::SslServerCredentialsOptions ssl_opts;
ssl_opts.pem_key_cert_pairs.push_back({
    read_file("server.key"),
    read_file("server.crt")
});
ssl_opts.pem_root_certs = read_file("ca.crt");

builder.AddListeningPort(addr, grpc::SslServerCredentials(ssl_opts));

// 客户端 TLS
grpc::SslCredentialsOptions ssl_opts;
ssl_opts.pem_root_certs = read_file("ca.crt");
ssl_opts.pem_private_key = read_file("client.key");
ssl_opts.pem_cert_chain = read_file("client.crt");

auto channel = grpc::CreateChannel("localhost:50051",
                                   grpc::SslCredentials(ssl_opts));

拦截器(Interceptor)

cpp
// 日志拦截器
class LoggingInterceptor : public grpc::experimental::Interceptor {
public:
    void Intercept(grpc::experimental::InterceptorBatchMethods* methods) override {
        if (methods->QueryInterceptionHookPoint(
            grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) {
            auto* meta = methods->GetSendInitialMetadata();
            std::cout << "RPC 调用开始\n";
        }
        methods->Proceed();
    }
};

关键认知

gRPC 的核心优势是强类型接口 + 高效二进制序列化 + HTTP/2 多路复用。Proto 文件是服务契约,先设计好接口再实现。生产环境必须启用 TLS。流式 RPC 适合大数据传输和实时通信场景。

系统学习 C++ 生态,深入底层架构