C++ gRPC服务RPC调用插桩方案咨询:如何消除重复代码?
gRPC C++服务的RPC调用插桩方案
需求背景
你在开发C++ gRPC服务时,需要实现两类通用逻辑:
- 记录每个请求的处理耗时
- 将所有服务事务写入"history"数据库
当前你在每个RPC方法中手动添加耗时统计代码,即使做了抽象仍存在大量重复,需要支持on_entry+on_exit逻辑的RPC插桩方案。
推荐解决方案
1. gRPC原生Server Interceptor(最优方案)
gRPC官方提供的实验性服务端拦截器(目前已趋于稳定),可以统一拦截所有RPC请求,完美实现on_entry和on_exit的钩子逻辑,完全避免重复代码。
实现示例:
拦截器类(处理耗时统计+事务写入)
#include <grpcpp/grpcpp.h> #include <chrono> #include <iostream> class ServiceInterceptor : public grpc::experimental::Interceptor { public: void Intercept(grpc::experimental::InterceptorBatchMethods* methods) override { // 请求进入钩子(on_entry):记录开始时间 if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA)) { start_time_ = std::chrono::steady_clock::now(); } // 请求退出钩子(on_exit):统计耗时+写入history数据库 if (methods->QueryInterceptionHookPoint(grpc::experimental::InterceptionHookPoints::POST_RECV_MESSAGE)) { auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start_time_); // 打印耗时 std::cout << "Request: " << methods->GetMethod()->name() << " duration " << duration.count() << " (ms)\n"; // 这里添加写入history数据库的事务逻辑 // save_to_history_db(methods->GetMethod()->name(), request_data, duration); } // 继续执行后续逻辑 methods->Proceed(); } private: std::chrono::steady_clock::time_point start_time_; };
拦截器工厂类
class ServiceInterceptorFactory : public grpc::experimental::ServerInterceptorFactoryInterface { public: grpc::experimental::Interceptor* CreateServerInterceptor(grpc::experimental::ServerRpcInfo* /*info*/) override { return new ServiceInterceptor(); } };
注册拦截器到服务器
int main() { std::string server_address("0.0.0.0:50051"); SmartHomeServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); // 注册拦截器工厂 std::vector<std::unique_ptr<grpc::experimental::ServerInterceptorFactoryInterface>> interceptors; interceptors.push_back(std::make_unique<ServiceInterceptorFactory>()); builder.experimental().SetInterceptorCreators(std::move(interceptors)); std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); return 0; }
2. C++ AOP库(编译期插桩)
如果需要更灵活的横切逻辑管理,可以使用**AspectC++**这类AOP库,在编译期自动为所有RPC方法注入on_entry和on_exit逻辑,无需修改业务代码。
核心示例:
#include <aspectc++/aspect.h> #include <chrono> #include <iostream> using namespace std; using namespace asp; // 定义切面,包含on_entry和on_exit逻辑 struct ServiceAspect : public aspect { // 进入RPC方法前执行 advice<void, before, mem_func("SmartHome::Service::*(...)")> on_entry() { // 存储开始时间到方法上下文 targ::context().start_time = chrono::steady_clock::now(); } // 退出RPC方法后执行 advice<void, after, mem_func("SmartHome::Service::*(...)")> on_exit() { auto duration = chrono::duration_cast<std::chrono::milliseconds>( chrono::steady_clock::now() - targ::context().start_time); std::cout << "Request: " << __func__ << " duration " << duration.count() << " (ms)\n"; // 写入history数据库逻辑 } }; // 为目标服务类绑定切面 ASPECTS(ServiceAspect, SmartHomeServiceImpl);
3. 通用模板封装(轻量方案)
如果不想依赖拦截器或AOP库,可以封装一个通用模板函数,将业务逻辑作为回调传入,统一处理前置和后置逻辑:
模板函数实现
template <typename Request, typename Reply, typename Handler> grpc::Status WrapRpc(Handler&& handler, grpc::ServerContext* context, const Request* request, Reply* reply) { // on_entry:记录开始时间 auto start = std::chrono::steady_clock::now(); // 执行实际业务逻辑 grpc::Status status = std::forward<Handler>(handler)(context, request, reply); // on_exit:统计耗时+写入数据库 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start); std::cout << "Request: " << __PRETTY_FUNCTION__ << " duration " << duration.count() << " (ms)\n"; // save_to_history_db(...) return status; }
业务方法中使用
class SmartHomeServiceImpl final : public SmartHome::Service { Status ChangeVolume(grpc::ServerContext* context, const ChangeVolumeRequest* request, ChangeVolumeReply* reply) override { return WrapRpc([this](auto ctx, auto req, auto rep) { // ... 实际业务逻辑 return grpc::Status::OK; }, context, request, reply); } };
内容的提问来源于stack exchange,提问作者Lorah Attkins
相关产品推荐
相关产品推荐

