智能 RPC框架 (C++)
RPC中文叫远程函数调用,它是一种通信方式,只是看起来像普通的函数调用。
它包括三个基本要素:
1:服务端注册相应的(服务)函数(用于调用方调用)
2:调用方通过函数调用的方式将一些信息和参数打包到消息,然后发送消息给被调用方。
3:被调用方收到消息后,提取信息和参数。调用相应函数。
被调用方不需要用户手动解析参数,而是由"包装代码"预先解析出来。
目前很多rpc框架都(设计)配有协议描述文件,通过代码生成,产生((含有)"包装代码")服务端的服务类或函数。
我不喜欢代码生成,我喜欢直接在代码中搞定它。
果然,我最近看到有朋友在一些脚本语言中做到这点。某些实现还不需要手动(预先)注册服务函数。
比如:
https://github.com/sniperHW/distri.lua/blob/master/examples/rpcserver.lua
https://github.com/akirayu101/GM_RPC/blob/master/gmrpc_lua/rpc_handlers/rpc_handler_sample.lua
https://github.com/akirayu101/GM_RPC/blob/master/gmrpc_py/rpc_handlers/rpc_handler_sample.py
然而我又不熟悉lua或python,所以我用C++11 来实现了它。
主要功能:
1:注册服务函数
void test5(string a, int b, map<int, map<int, string>> vlist)
{
} rpc rpc_server; /*rpc服务器*/
rpc_server.def("test5", test5);
2:客户端调用远程函数
rpc rpc_client; /*rpc客户端*/
rpc_client.call("test5", "a", 1, mlist, [&upvalue](int a, int b){
upvalue++;
cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << endl;
});
其中mlis是一个map<int,map<int,string>>类型变量。 rpc_client.call 的返回值是一个string,它表示此次call的消息。
我们可以把它(string 消息)通过网络发送给服务器。在这里(测试)我们直接通过下面的方式传递给服务端。
!!!注意!!!:call的最后一个参数可以是一个lambda,它表示处理此rpc返回值。 如果不是一个lambda,则它也是rpc调用参数。
3:服务端处理rpc request
rpc_server.handleRpc(rpc_request_msg);
其中 rpc_request_msg为接受到的网络消息(字符串)。
这样就会自动调用到我们的 test5 函数。 并且形参已经(自动)准备OK。你只需要在test5 里使用这些参数即可。(不用关心网络消息协议)。
4:被调用方可以返回数据给调用方
rpc_response_str = rpc_server.reply(1, 1, 2); /* (1,1,2)中的1为调用方的req_id, (1,2)为返回值 */
rpc_client.handleResponse(rpc_response_str);
上面代码通过 rpc_server.reply返回消息给客户端。 然后客户端模拟收到消息后通过 rpc_client.handleResponse(rpc_response_str)
会回调rpc_client.call() 时 所传递的lambda回调函数。
注意:以上 服务函数(譬如test5)和rpc 返回值处理函数(譬如那个lambda)的参数 是任意个数,且"任意"类型
(支持 int,string,JsonObject-json对象,vector<int>,vector<string>, map<int,string>,map<string,int>,map<string,string>, map<int/string, 前述所有类型/递归> )
整个测试代码:
void test1(int a, int b)
{
cout << "in test1" << endl;
cout << a << ", " << b << endl;
} void test2(int a, int b, string c)
{
cout << "in test2" << endl;
cout << a << ", " << b << ", " << c << endl;
} void test3(string a, int b, string c)
{
cout << "in test3" << endl;
cout << a << ", " << b << ", " << c << endl;
} void test4(string a, int b)
{
cout << "in test4" << endl;
cout << a << "," << b << endl;
} void test5(string a, int b, map<int, map<int, string>> vlist)
{
} void test6(string a, int b, map<string, int> vlist)
{
} void test7()
{
cout << "in test7" << endl;
} int main()
{
int upvalue = 10;
using namespace dodo; rpc rpc_server; /*rpc服务器*/
rpc rpc_client; /*rpc客户端*/ rpc_server.def("test4", test4);
rpc_server.def("test5", test5);
rpc_server.def("test7", test7); string rpc_request_msg; /* rpc消息 */
string rpc_response_str; /* rpc返回值 */ {
rpc_request_msg = rpc_client.call("test7"); rpc_server.handleRpc(rpc_request_msg);
} map<int, string> m1;
m1[1] = "Li";
map<int, string> m2;
m2[2] = "Deng";
map<int, map<int, string>> mlist;
mlist[100] = m1;
mlist[200] = m2; {
rpc_request_msg = rpc_client.call("test5", "a", 1, mlist, [&upvalue](int a, int b){
upvalue++;
cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << endl;
}); rpc_server.handleRpc(rpc_request_msg);
} {
rpc_request_msg = rpc_client.call("test5", "a", 1, mlist, [&upvalue](string a, string b, int c){
upvalue++;
cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << ", c:" << c << endl;
}); rpc_server.handleRpc(rpc_request_msg);
} {
rpc_request_msg = rpc_client.call("test4", "a", 1);
rpc_server.handleRpc(rpc_request_msg);
} /* 模拟服务器通过reply返回数据给rpc client,然后rpc client处理收到的rpc返回值 */
{
rpc_response_str = rpc_server.reply(1, 1, 2); /* (1,1,2)中的1为调用方的req_id, (1,2)为返回值 */
rpc_client.handleResponse(rpc_response_str);
} {
rpc_response_str = rpc_server.reply(2, "hello", "world", 3);
rpc_client.handleResponse(rpc_response_str);
} cin.get();
return 0;
}
RPC"框架"代码地址: https://github.com/IronsDu/accumulation-dev/blob/master/utils/rpc_test.cpp 。
欢迎讨论。
---update:
我们来看看最新战果:
class Player : public dodo::rpc
{
public:
Player()
{
registerHandle("player_attack", &Player::attack);
registerHandle("player_hi", &Player::hi);
} private:
template<typename... Args>
void registerHandle(string name, void (Player::*callback)(Args...))
{
def(name.c_str(), [this, callback](Args... args){
(this->*callback)(args...);
});
} private:
void attack(string target)
{
cout << "attack:" << target << endl;
} void hi(string i, string j)
{
cout << i << j << endl;
}
}; Player rpc_server; /*rpc服务器*/
Player rpc_client; /*rpc客户端*/ rpc_request_msg = rpc_client.call("player_attack", "Li Lei");
rpc_server.handleRpc(rpc_request_msg);
rpc_request_msg = rpc_client.call("player_hi", "Hello", "World");
rpc_server.handleRpc(rpc_request_msg);
每一个Player就是一个rpc,再结合网络库,就能很轻松的开发业务逻辑。
智能 RPC框架 (C++)的更多相关文章
- rpc框架之 thrift 学习 1 - 安装 及 hello world
thrift是一个facebook开源的高效RPC框架,其主要特点是跨语言及二进制高效传输(当然,除了二进制,也支持json等常用序列化机制),官网地址:http://thrift.apache.or ...
- RPC框架实践之:Apache Thrift
一.概述 RPC(Remote Procedure Call)即 远程过程调用,说的这么抽象,其实简化理解就是一个节点如何请求另一节点所提供的服务.在文章 微服务调用链追踪中心搭建 一文中模拟出来的调 ...
- SpringCloud Alibaba (四):Dubbo RPC框架
Dubbo简介 Apache Dubbo |ˈdʌbəʊ| 是一款高性能.轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现.致 ...
- RPC 框架 Dubbo 从理解到使用(一)
技术架构演变 单一应用架构 通俗地讲,"单体应用(monolith application)"就是将应用程序的所有功能都打包成一个独立的单元.当网站流量很小时,只需一个应用,将所有 ...
- 什么是RPC,RPC好处,常用的RPC框架
RPC简介 RPC(Remote Procedure Call Protocol)远程过程调用协议.一个通俗的描述是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用 ...
- 【万字长文】Dubbo 入门总结 ,一款高性能的 Java RPC 框架
这篇文章是我学习整理 Dubbo 的一篇文章,首先大部分内容参考了官网 + 某硅谷的视频,内容讲解进行了重新编排,40多张图片,也都是我修改重制的,虽然一万多字,但是其实也可以看出来,更多的内容集中在 ...
- RPC框架实现 - 通信协议篇
RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面.其对业务隐藏了底层通信过程(TCP/UDP.打包/解包.序列化/反序列化),使 ...
- 微博轻量级RPC框架Motan
Motan 是微博技术团队研发的基于 Java 的轻量级 RPC 框架,已在微博内部大规模应用多年,每天稳定支撑微博上亿次的内部调用.Motan 基于微博的高并发和高负载场景优化,成为一套简单.易用. ...
- 一个轻量级分布式RPC框架--NettyRpc
1.背景 最近在搜索Netty和Zookeeper方面的文章时,看到了这篇文章<轻量级分布式 RPC 框架>,作者用Zookeeper.Netty和Spring写了一个轻量级的分布式RPC ...
随机推荐
- poj 3020 Antenna Placement(最小路径覆盖 + 构图)
http://poj.org/problem?id=3020 Antenna Placement Time Limit: 1000MS Memory Limit: 65536K Total Sub ...
- android 简易定时器
定时器 1.在android 应用开发当中,很多时候都要用到定时器,而要实现定时器更多的时候要用到两个类:Timer,和TimerTask 2.API对Timer的解释是:
- css中的img和input标签
一般情况下,行内元素设置宽高是无效的,常见的有a标签.img和input也属于行内元素,但他们却可以设置宽高!!!! 查阅了一些资料才明白,原来css的元素还有另外一种分类方法,可替换元素,不可替换元 ...
- C#实现Combobox自动匹配字符
不多说了,如图,应客户要求,下拉框中需要自动匹配字符,可能有些人一早就对此很熟,但相对于我还是首次使用,还是花了一点时间,现记录下来,也希望能帮助大家更好的理解. 首先要设定Combobox的Drop ...
- C#对HTML转译需要注意的问题
在做B/S程序时我们多少会用到一点HTML特殊符号转译. 如:“&”——>“&” , "<"——>"<" , " ...
- Swift学习笔记十二
方法 方法就是和某种特定类型相关联的函数.类.结构体.枚举都可以定义实例方法和类型方法.类型方法和OC中的类方法类似. 结构体和枚举也可以定义方法是Swift与C/OC之间很大的一个区别,在OC中,只 ...
- 无线网卡的查看与配置——iw,iwconfig,ethtool
摘要:在linux上,我们经常需要把一台笔记本设置成一个共享wifi上网的路由器.如果我们需要把本机配置成一台无线AP,就需要查看网卡的相关信息和进行对应配置.其中iw.iwconfig和ethtoo ...
- SQL Server数据库大型应用解决方案总结
随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的互联网应用,每天百万级甚至上亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的问题. 一 ...
- 007_尚学堂_高明鑫_android 之项目的打包apk与apk的反编译
http://www.tudou.com/programs/view/kMQlxff8evM/
- Android游戏框架之基础之AA碰撞系统
AA 碰撞体 就是将所有的物体设置为矩形框进行碰撞计算.下面是代码 /* * Copyright (C) 2010 The Android Open Source Project * * Licens ...