buttonrpc - 框架解析
参考资料
buttonrpc源码 (opens new window)
涉及的知识点
- ZeroMQ 作为网络层
- 需要熟悉一般的请求--应答消息传输方式
- 使用c++14开发
- 智能指针 unique_ptr
- 模板函数编程
- tuple、std::is_same、std::decay、std::forward
Features
- 轻量级,跨平台,简单易用
- 服务端可以绑定自由函数,类成员函数,std::function对象
- 服务端可以绑定参数是任意自定义类型的函数
- 客户端与服务端自动重连机制
- 客户端调用超时选项,ZeroMQ自带
- 客户端与服务端启动顺序随意
简介
客户端和服务端的stub都是buttonrpc对象,它提供了客户端和服务端的调用方法as_client和as_server。
客户端的主动调用过程
将封装函数名和函数参数信息的Serializer对象,转换成zmq::message_t对象,并通过send函数发送。
服务端的被动调用过程:
- recv函数收到客户端的消息内容后,会先解出函数名,再看是否注册过相应函数,若注册过,则调用函数并填入对应参数。 最终,将函数结果通过send函数返回给客户端。
- 备注: 同时客户端和服务端均需要实现相同的 Serializer& operator >> 和 Serializer& operator << 便于按照指定格式解析数据。
rpc调用流程图:
框架解析
StreamBuffer ———— 字节流类,继承vector<char>,便于按字节提取数据,有_curpos变量,指示当前所指位置
Serializer ———— 序列化类,按照特定字节流顺序组织结构,根据输入参数的类型,将其转换为char *结构
buttonrpc ————rpc框架类,以ZeroMQ作为网络层,封装借助C++14的tuple等特性,完成调用参数列表转换成 序列化类结构的数据进行传输。主要思想:服务端注册函数内容,开发特定端口;客户端根据函数名和参数列表进行调用。
服务注册与发现 ———— 服务端bind函数时,将函数名和调用实体封装成map结构。每次客户端调用时,服务端均会先解析成函数名。判断是否已经注册。
1
2
3
4
5
2
3
4
5
c++14用到的知识点
unique_ptr的构造和删除器
m_socket = std::unique_ptr<zmq::socket_t, std::function<void(zmq::socket_t*)>>(new zmq::socket_t(m_context, ZMQ_REP), [](zmq::socket_t* sock){ sock->close(); delete sock; sock =nullptr;});
unique_ptr<T, D> m_socket; //T对应构造,D对应删除器
void Deleter(zmq::socket_t* sock)
{
sock->close(); //关闭连接
delete sock; //删除对象指针
sock = nullptr;
}
//删除器的类型的函数指针: decltype(Deleter)* 等效于 void (*) (zmq::socket_t*);
m_socket = std::unique_ptr<zmq::socket_t, decltype(Deleter)*>>(new zmq::socket_t(m_context, ZMQ_REP), Deleter);
还待研究:
最终用lambda删除器实现:
auto deleter = [](zmq::socket_t *sock)
{
sock->close(); //关闭连接
delete sock; //删除对象指针
sock = nullptr;
};
_socket = std::unique_ptr<zmq::socket_t, decltype(deleter)>(new zmq::socket_t(_context, ZMQ_REQ), deleter);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
智能指针(smarter pointer)自定义删除器(deleter) 的方法:
智能指针包含两种"shared_ptr"和"unique_ptr", 由于两种指针的实现方式不同, 所以传递删除器的方式也不同;
"shared_ptr"的传递删除器(deleter)方式比较简单, 只需要在参数中添加具体的删除器函数名, 即可; 注意是单参数函数;
"unique_ptr"的删除器是函数模板(function template), 所以需要在模板类型传递删除器的类型(即函数指针(function pointer)), 再在参数中添加具体删除器;
定义函数指针的类型, 包含三种方法(typedef, typedef decltype, using), 也可以直接传递decltype;
1
2
3
4
5
6
2
3
4
5
6
#include <iostream>
#include <memory>
using namespace std;
void deleter (int* ptr) {
delete ptr;
ptr = nullptr;
std::clog << "shared_ptr delete the pointer." << std::endl;
}
int main (void) {
//定义函数类型
typedef void (*tp) (int*);
typedef decltype (deleter)* dp;
using up = void (*) (int*);
std::shared_ptr<int> spi(new int(10), deleter);
std::shared_ptr<int> spi2(new int, deleter);
spi2 = std::make_shared<int>(15);
std::cout << "*spi = " << *spi << std::endl;
std::cout << "*spi2 = " << *spi2 << std::endl;
//unique_ptr是模板函数需要删除器(deleter)类型, 再传入具体的删除器
std::unique_ptr<int, decltype(deleter)*> upi(new int(20), deleter);
std::unique_ptr<int, tp> upi2(new int(25), deleter);
std::unique_ptr<int, dp> upi3(new int(30), deleter);
std::unique_ptr<int, up> upi4(new int(35), deleter);
std::cout << "*upi = " << *upi << std::endl;
std::cout << "*upi2 = " << *upi2 << std::endl;
std::cout << "*upi3 = " << *upi3 << std::endl;
std::cout << "*upi4 = " << *upi4 << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
运行结果:
[root@iZuf61kbf845xt6tz10abgZ ptr]# g++ -o ptr_main -std=c++11 ptr_main.cpp
[root@iZuf61kbf845xt6tz10abgZ ptr]# ./ptr_main
shared_ptr delete the pointer.
*spi = 10
*spi2 = 15
*upi = 20
*upi2 = 25
*upi3 = 30
*upi4 = 35
shared_ptr delete the pointer.
shared_ptr delete the pointer.
shared_ptr delete the pointer.
shared_ptr delete the pointer.
shared_ptr delete the pointer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::is_same 判断类型是否一致
位于头文件<type_traits>
中
这个结构体作用很简单,就是两个一样的类型会返回true
bool isInt = std::is_same<int, int>::value; //为true
1
enable_if 的原理
enable_if 的定义类似于下面的代码:(只有 Cond = true 时定义了 type)
template<bool Cond, class T = void> struct enable_if {};
template<class T> struct enable_if<true, T> { typedef T type; };
1
2
2
std::forward 完美转发的原理
http://www.voidcn.com/article/p-uvuqppah-xy.html
https://blog.csdn.net/hzhsan/article/details/46342651
- typedef typename std::vector
::size_type size_type;
typedef创建了存在类型的别名,而typename告诉编译器std::vector<T>::size_type是一个类型而不是一个成员。
1
- template
template<typename T>
表示T是一个类型, 在模版实例化时可以替换任意类型,不仅包括内置类型(int等),也包括自定义类型class。 换句话说,在template<typename Y>和template<class Y>中,
typename和class的意义完全一样。
1
2
3
2
3
template<typename... Args>
void package_params(Serializer& ds, const std::tuple<Args...>& t)
{
package_params_impl(ds, t, std::index_sequence_for<Args...>{});
}
中间 std::index_sequence_for ——> make_index_sequence
定义辅助类模板 std::index_sequence_for ,以转换任何类型参数包为同长度的下标序列:
template<class... T>
using index_sequence_for = std::make_index_sequence<sizeof...(T)>;
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
std::decay
Obtains the decay type of T.
If T is a reference type, the type referrered to by T.
Otherwise, T.
std::decay就是对一个类型进行退化处理
1
2
3
4
2
3
4