You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C++ gRPC中脱离Protobuf,使用自定义JSON序列化实现服务的技术咨询

在C++ gRPC中脱离Protobuf,使用自定义JSON序列化实现服务的技术咨询

我来帮你一步步解决这个在C++ gRPC里完全脱离Protobuf、用JSON做序列化的问题,结合你已经写的代码给出具体可落地的方案:

1. 自定义序列化/反序列化:SerializationTraits是否足够?

你写的grpc::SerializationTraits<nlohmann::json>特化是完全正确的方向,这是gRPC官方提供的用于替换默认Protobuf序列化的扩展点,但单独的SerializationTraits还不够——因为gRPC默认的服务方法处理器是为Protobuf消息设计的,你需要配合自定义的方法处理器,才能让gRPC主动调用你的JSON序列化逻辑。

先优化并确认你的SerializationTraits实现:

#include <grpcpp/grpcpp.h>
#include <nlohmann/json.hpp>

namespace grpc {
template <>
class SerializationTraits<nlohmann::json> {
public:
    static grpc::Status Serialize(const nlohmann::json& json, grpc::ByteBuffer* buffer, bool* own_buffer) {
        std::string s = json.dump();
        // 用STEAL_FROM_STRING避免内存拷贝,提升效率
        grpc::Slice slice(std::move(s), grpc::Slice::STEAL_FROM_STRING);
        *buffer = grpc::ByteBuffer(&slice, 1);
        *own_buffer = true;
        return grpc::Status::OK;
    }

    static grpc::Status Deserialize(grpc::ByteBuffer* buffer, nlohmann::json* json) {
        std::vector<grpc::Slice> slices;
        if (!buffer->Dump(&slices).ok()) {
            return grpc::Status(grpc::StatusCode::INTERNAL, "Failed to extract data from buffer");
        }

        std::string data;
        for (const auto& slice : slices) {
            data.append(reinterpret_cast<const char*>(slice.begin()), slice.size());
        }

        try {
            *json = nlohmann::json::parse(data);
        } catch (const std::exception& e) {
            return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, std::string("JSON parse error: ") + e.what());
        }
        return grpc::Status::OK;
    }
};
} // namespace grpc

2. 完全脱离.proto手动注册gRPC服务与方法

gRPC的C++ API支持完全手动注册服务和方法,不需要protoc生成任何代码。核心是继承grpc::Service,手动创建RpcServiceMethod并通过AddMethod注册,同时实现自定义的RpcMethodHandler来衔接JSON序列化和业务逻辑。

步骤1:实现自定义JSON方法处理器

这个处理器会调用你定义的SerializationTraits来完成序列化/反序列化,同时触发业务逻辑:

#include <functional>

template <typename RequestType, typename ResponseType>
class JsonUnaryMethodHandler : public grpc::RpcMethodHandler {
public:
    // 定义业务逻辑的函数签名:接收请求、输出响应、可访问ServerContext
    using HandlerFunc = std::function<grpc::Status(const RequestType&, ResponseType*, grpc::ServerContext*)>;

    explicit JsonUnaryMethodHandler(HandlerFunc handler) : handler_(std::move(handler)) {}

    grpc::Status RunHandler(const grpc::RpcServiceMethod* method,
                            grpc::ServerContext* context,
                            grpc::ServerUnaryReactor* reactor,
                            grpc::ByteBuffer* req_buffer,
                            grpc::ByteBuffer* resp_buffer) override {
        // 1. 反序列化JSON请求
        RequestType request;
        auto status = grpc::SerializationTraits<RequestType>::Deserialize(req_buffer, &request);
        if (!status.ok()) {
            return status;
        }

        // 2. 执行业务逻辑
        ResponseType response;
        status = handler_(request, &response, context);
        if (!status.ok()) {
            return status;
        }

        // 3. 序列化JSON响应
        bool own_buffer;
        return grpc::SerializationTraits<ResponseType>::Serialize(response, resp_buffer, &own_buffer);
    }

private:
    HandlerFunc handler_;
};

步骤2:手动注册服务与方法

创建自己的服务类,在构造函数里完成方法的手动注册:

class JsonEchoService : public grpc::Service {
public:
    JsonEchoService() {
        // 1. 定义方法元数据:方法全名(格式为/服务名/方法名)、RPC类型
        auto* echo_method = new grpc::RpcServiceMethod(
            "/JsonEchoService/Echo",
            grpc::RpcMethod::NORMAL_RPC,
            nullptr // 不需要Protobuf消息描述,传nullptr即可
        );

        // 2. 绑定业务逻辑到处理器
        auto echo_handler = new JsonUnaryMethodHandler<nlohmann::json, nlohmann::json>(
            [this](const nlohmann::json& req, nlohmann::json* resp, grpc::ServerContext* context) -> grpc::Status {
                // 示例业务逻辑:返回原请求,并添加服务端标识
                *resp = req;
                (*resp)["server_tag"] = "JSON gRPC server v1.0";
                return grpc::Status::OK;
            }
        );

        // 3. 将方法注册到服务
        AddMethod(echo_method, echo_handler);
    }
};

步骤3:启动服务端

和普通gRPC服务端启动逻辑一致,只是注册我们手动创建的服务:

void RunServer() {
    std::string server_addr("0.0.0.0:50051");
    JsonEchoService service;

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

    std::unique_ptr<grpc::Server> server = builder.BuildAndStart();
    std::cout << "JSON gRPC server running on " << server_addr << std::endl;
    server->Wait();
}

步骤4:客户端手动调用(无需stub)

客户端可以用gRPC的Generic API调用手动注册的方法,配合SerializationTraits完成序列化/反序列化:

grpc::Status CallJsonUnaryMethod(grpc::ChannelInterface* channel,
                                 const std::string& method_full_name,
                                 const nlohmann::json& req,
                                 nlohmann::json* resp,
                                 grpc::ClientContext* context = nullptr) {
    // 序列化JSON请求
    grpc::ByteBuffer req_buffer;
    bool own_buffer;
    auto status = grpc::SerializationTraits<nlohmann::json>::Serialize(req, &req_buffer, &own_buffer);
    if (!status.ok()) {
        return status;
    }

    // 发起Generic RPC调用
    grpc::ByteBuffer resp_buffer;
    grpc::GenericStub stub(channel);
    auto ctx = context ? context : new grpc::ClientContext();
    status = stub.UnaryCall(ctx, method_full_name, req_buffer, &resp_buffer);
    if (!status.ok()) {
        return status;
    }

    // 反序列化JSON响应
    return grpc::SerializationTraits<nlohmann::json>::Deserialize(&resp_buffer, resp);
}

// 客户端示例代码
void RunClient() {
    auto channel = grpc::CreateChannel("0.0.0.0:50051", grpc::InsecureChannelCredentials());
    nlohmann::json req = {{"client_msg", "Hello from JSON client!"}, {"timestamp", 1690000000}};
    nlohmann::json resp;

    auto status = CallJsonUnaryMethod(channel.get(), "/JsonEchoService/Echo", req, &resp);
    if (status.ok()) {
        std::cout << "Received response:\n" << resp.dump(4) << std::endl;
    } else {
        std::cout << "RPC failed: " << status.error_code() << " - " << status.error_message() << std::endl;
    }
}

3. 彻底移除CMake中的Protobuf依赖

如果你的项目完全不需要Protobuf,可以在引入gRPC时禁用相关编译选项:

cmake_minimum_required(VERSION 3.18)
project(json_grpc_demo)

set(CMAKE_CXX_STANDARD 17)

# 引入nlohmann/json
include(FetchContent)
FetchContent_Declare(
    nlohmann_json
    GIT_REPOSITORY https://github.com/nlohmann/json
    GIT_TAG v3.11.2
)
FetchContent_MakeAvailable(nlohmann_json)

# 引入gRPC,禁用Protobuf和所有代码生成插件
FetchContent_Declare(
    grpc
    GIT_REPOSITORY https://github.com/grpc/grpc
    GIT_TAG v1.59.0
)
set(gRPC_BUILD_PROTOBUF OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_CPP_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_CSHARP_EXT OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_NODE_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PHP_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_PYTHON_PLUGIN OFF CACHE BOOL "" FORCE)
set(gRPC_BUILD_GRPC_RUBY_PLUGIN OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(grpc)

# 编译服务端和客户端
add_executable(json_grpc_server server.cpp)
target_link_libraries(json_grpc_server
    gRPC++
    gRPC++_core
    nlohmann_json::nlohmann_json
)

add_executable(json_grpc_client client.cpp)
target_link_libraries(json_grpc_client
    gRPC++
    gRPC++_core
    nlohmann_json::nlohmann_json
)

关键注意事项

  • SerializationTraits特化必须放在grpc命名空间下,且在所有使用nlohmann::json作为gRPC消息类型的代码之前包含
  • 手动注册的方法全名格式必须严格遵循/服务名/方法名,客户端调用时需完全匹配
  • 如果需要支持流式RPC(ServerStreaming/ClientStreaming/BidiStreaming),只需实现对应类型的RpcMethodHandler子类,逻辑和Unary类似,只需处理流的读写逻辑

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:07:59