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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 08:44:55