易用的C++ RPC服务框架 - pioneer - 5 - 技术实现:函数的序列化
来源:互联网 发布:晋业进销存软件8.25 编辑:程序博客网 时间:2024/06/05 16:33
在过去的两年时间里,我一直在用C++11写分布式数据库。在分布式系统中,远程方法调用是一个大麻烦。能不能像本地方法一样调用远程方法?
能不能以异步的方式调用远程方法而调用线程不阻塞?能不能广播调用远程方法?能不能自动将异常信息带到客户端来处理?
于是我写了一个RPC框架pioneer,支持以下特性:
1.针对集群来设计
2.和本地函数几乎一样的调用方法
4.支持同步调用和异步调用
5.支持有返回值/无返回值,对无返回值的调用进行优化
6.支持一对一、一对多、多播、广播、可靠广播调用
7.支持异常处理
8.不考虑跨语言
在上一篇文章中,我介绍了在C++11中,如何借助std::tuple和可变模板参数来序列化一个函数,并且给出了一个通用实现和测试代码:
实现细节见:https://github.com/galaxyeye/atlas/blob/master/atlas/serialization/function.h
测试程序:https://github.com/galaxyeye/atlas/blob/master/libs/serialization/test/function.cpp
使用atlas::serialization::function,可以很轻松地将一个函数序列化,反序列化以及反序列化后执行。
其秘诀在于使用了三个威力强大的武器:std::tuple, std::function和可变模板参数。
那么有几个问题:
1. 如何序列化std::tuple? 标准库中并没用对std::tuple的输入输出实现,也就是说,标准库中没有实现:
template<typename... Args> std::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t);
和:
template<typename... Args> std::istream& operator>>(std::istream& is, std::tuple<Args...>& t);
boost::serialization中也没有对应实现。唉,得自己动手了。
2. 我们找到了序列化可变参数包的方法,就是引入一个中间变量std::tuple来保存这个参数包,那么我们知道函数function, 和一个参数包tuple,怎么样把tuple里的参数解出来供函数function调用?
对于第一个问题,通常有两种解决方法:第一种是{我的解决方法},另一种是~{我的解决方法}。
我的解决方法纯粹使用函数进行编译期模板推导,代码平白如话:
template<size_t idx, typename Archive, typename ... Elements> void aux_serialize(Archive& ar, std::tuple<Elements...>& t, single_parameter_pack_tag) { ar & std::get<idx>(t); } template<size_t idx, typename Archive, typename ... Elements> void aux_serialize(Archive& ar, std::tuple<Elements...>& t, not_single_parameter_pack_tag) { ar & std::get<idx>(t); aux_serialize<idx + 1>(ar, t, atlas::is_last_parameter<idx, Elements...>()); } template<typename Archive, typename ... Elements> void serialize(Archive& ar, std::tuple<Elements...>& t, last_parameter_tag) { ar & std::get<0>(t); } template<typename Archive, typename ... Elements> void serialize(Archive& ar, std::tuple<Elements...>& t, not_last_parameter_tag) { aux_serialize<0>(ar, t, std::false_type()); }
boost兼容的序列化函数:
namespace boost { namespace serialization { template<typename Archive, typename ... Elements> Archive& serialize(Archive& ar, std::tuple<Elements...>& t, const unsigned int version) { atlas::serialize(ar, t, atlas::is_single_parameter_pack<Elements...>()); return ar; } } // serialization} // boost
完整的代码在这里。这里用到几个编译期函数:
is_single_parameter判断一个参数包是否为单个参数
is_last_parameter判断一个参数是否为参数包中最后一个参数
用这套方案,可以解决很多编译期问题,譬如打印出一个std::tuple,代码几乎一模一样。
其他的解决方案,一般是基于class的模板参数推导,不赘述。
对于第二个问题,facebook的folly提出来一个方案,apply_tuple。folly::apply_tuple是这么用的:
int x = atlas::apply_tuple(std::plus<int>(), std::make_tuple(12, 12));assert(x == 24);
你可以看到,这就是给定一个函数,以及包含函数的参数的打包的std::tuple,怎么调用这个函数。所以在前面的文章里的function_wrapper的operator就可以这么实现:
template<typename Res, typename ... Args> class function_wrapper<Res(Args...)> { public: Res operator()() const { return apply_tuple(_f, _args); } public: std::function<Res(Args...)> _f; std::tuple<Args...> _args; };
至此,一个可序列化的函数包装器就成型了。
- 易用的C++ RPC服务框架 - pioneer - 5 - 技术实现:函数的序列化
- 易用的C++ RPC服务框架 - pioneer - 4 - 技术实现:函数的序列化
- 易用的C++ RPC服务框架 - pioneer - 3 - 编写你自己的控制台命令
- 易用的C++ RPC服务框架 - pioneer - 1 - 设计目标和应用场景
- 易用的C++ RPC服务框架 - pioneer - 2 - 编译和执行演示程序
- 支持多序列化的RPC框架avro-rpc
- RPC框架的实现
- 基于Netty的RPC简单框架实现(三):Kryo实现序列化
- 一起写RPC框架(五)RPC网络模块的搭建三 序列化
- 4.RPC框架的简单实现(服务发布-ServiceBean实现)
- 7.RPC框架的简单实现(服务引用-ReferenceBean实现与RMI协议)
- KNL_NETWORK实现二进制的RPC服务
- RPC框架-对象序列化
- 一个简单的rpc框架的实现
- 一个简单的rpc框架的实现
- 最简单的Rpc框架的实现
- 简单的RPC框架的实现
- 编写自己rpc框架——java rpc的实现
- MathContext的例子
- poj1833 排列
- 一步一步学数据结构之1--n(二叉树遍历--四种方法--递归式)
- mmc换硬币问题
- vi / vim 删除以及其它命令
- 易用的C++ RPC服务框架 - pioneer - 5 - 技术实现:函数的序列化
- 【转载】Android基于HTTP header的用户Authentication
- leetcode length of last word
- 书写CSS的5个小技巧
- 如何证明非方阵的矩阵是否可逆
- Google C++编程命名约定
- 3dmax中cs骨骼教程
- [3dmax教程] 人物+骨骼+蒙皮+动画教程
- .在二元树中找出和为某一值的所有路径