RPC 路由器类(router 的实现,用于在服务端或客户端中根据函数名(或其哈希值)来注册和调用远程过程。这个类使用了现代 C++ 技术(如模板元编程、tuple 解包、lambda 表达式、std::function 等),具有良好的扩展性和类型安全性。

整体结构概述

类定义

class router : asio::noncopyable {
  • 继承自 asio::noncopyable:禁止拷贝构造与赋值操作。
  • 使用了命名空间嵌套(如 rest_rpc::rpc_service::router)。

核心成员变量

std::unordered_map<uint32_t, std::function<void(...)>> map_invokers_;
std::unordered_map<uint32_t, std::string> key2func_name_;
  • map_invokers_:核心映射表,将函数签名对应的 hash 值(uint32_t)映射到一个可调用对象(invoker)。
  • key2func_name_:记录函数名与 hash 值的对应关系,便于调试或错误提示。

枚举和结构体定义

enum class router_error { ok, no_such_function, has_exception, unkonw };

struct route_result_t {
  router_error ec = router_error::unkonw;
  std::string result;
};

定义了路由错误类型和路由结果结构体。

helper_t模板类

template <typename Tuple, bool is_pub> class helper_t {
public:
  helper_t(Tuple &tp) : tp_(tp) {}
  void operator()() {}
private:
  Tuple &tp_;
};

template <typename Tuple> class helper_t<Tuple, true> {
public:
  helper_t(Tuple &tp) : tp_(tp) {}
  void operator()() {
    auto &arg = std::get<std::tuple_size<Tuple>::value - 1>(tp_);
    msgpack_codec codec;
    arg = codec.unpack<std::string>(arg.data(), arg.size());
  }
private:
  Tuple &tp_;
};

这是一个辅助模板类,用于处理发布/订阅模式下的参数解包。

注册函数接口(register_handler)

提供了两个重载版本的 register_handler(),支持注册普通函数和成员函数。

1. 注册非成员函数(静态函数或 lambda)

template <bool is_pub = false, typename Function>
void register_handler(std::string const &name, Function f, bool pub = false)
  • 计算函数名的 MD5 哈希作为唯一标识符。
  • 检查是否重复注册。
  • 调用 register_nonmember_func<is_pub>(key, f) 完成实际注册。

2. 注册成员函数

template <bool is_pub = false, typename Function, typename Self>
void register_handler(std::string const &name, const Function &f, Self *self)
  • 同样计算 hash 并检查重复。
  • 调用 register_member_func<is_pub>(key, f, self) 注册成员函数。

注册内部实现(register_nonmember_func / register_member_func)

这两个函数负责将用户注册的函数包装成统一格式,并存入 map_invokers_ 中。

典型处理逻辑:

  1. 将原始函数封装为一个 lambda。
  2. 使用 msgpack_codec 解码传入的字符串参数为 tuple。
  3. 使用 helper 工具类(如 helper_t<args_tuple, is_pub>)进行参数验证(可能是权限校验或参数数量检查)。
  4. 调用 call() 或 call_member() 来执行函数并返回结果。

通用调用辅助函数(call / call_member)

这两个函数实现了对任意函数签名的支持,通过 std::tuple 解包参数并转发给目标函数。

call()

template <typename F, typename... Args>
static typename std::enable_if<!std::is_void<...>::value>::type
call(const F &f, std::weak_ptr<connection> ptr, std::string &result, std::tuple<Args...> tp)
  • 支持返回值的函数。
  • 使用 call_helper() 执行函数调用。
  • 使用 msgpack_codec 打包结果。

call_member()

template <typename F, typename Self, typename... Args>
static void call_member(const F &f, Self *self, std::weak_ptr<connection> ptr, std::string &result, std::tuple<Args...> tp)
  • 类似于 call(),但适用于成员函数。
  • 使用 call_member_helper() 来调用成员函数指针。

路由调用入口(route())

这是整个路由器的核心方法,用于根据函数 hash 值调用对应的函数。

template <typename T>
route_result_t route(uint32_t key, nonstd::string_view data, std::weak_ptr<T> conn)

主要逻辑:

  1. 查找 hash 对应的 invoker。
  2. 如果找不到,返回错误信息。
  3. 否则调用 invoker 函数,传入连接指针、数据和输出结果。
  4. 返回包含结果和错误码的 route_result_t

其他实用方法

方法功能
remove_handler(name)删除已注册的函数
get_name_by_key(key)根据 hash 获取函数名(调试/日志用)
call_helper() / call_member_helper()辅助调用函数,利用 index_sequence 解包 tuple 参数
helper_t可能用于参数合法性检查(如 pub/sub 场景下的特殊处理)

关键类型与工具类说明

1. msgpack_codec

  • 用于序列化/反序列化消息体,基于 MessagePack 协议。
  • 支持从 string_view 解包为 tuple,也支持打包结果。

2. function_traits<Function>

  • 提取函数签名信息(如参数个数、参数类型等)。
  • 是实现泛型调用的关键。

3. nonstd::index_sequence / make_index_sequence

  • 编译期生成索引列表,用于解包 std::tuple

4. route_result_t

  • 包含函数调用结果(如 OK、异常等)和实际响应内容。

设计总结

特性描述
泛型支持支持任意函数签名(带返回值、无返回值、成员函数)
高性能使用 hash 快速查找函数
安全性使用 std::weak_ptr<connection> 防止循环引用
错误处理异常捕获、统一返回格式
扩展性强易于添加新的调用方式或中间件逻辑
支持 pub/sub通过 is_pub 模板参数区分不同调用语义

  router是一个高度泛型、模块清晰、设计优雅的 RPC 路由器类。它结合了现代 C++ 的多种高级特性,能够很好地支持远程函数注册、调用、错误处理和序列化通信。无论是构建高性能 RPC 服务端还是客户端,都具备很高的实用性。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐