在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
相关产品推荐
相关产品推荐

