buttonrpc解析—server篇


前言

关于buttonrpc.hpp的解析

server

我们从server.cpp的main函数部分来学习这个hpp文件

int main() {
   buttonrpc server;  
    server.as_server(5555);
    //server.bind("redis_command", redis_command);
    RedisServer::getInstance()->start();
    server.bind("redis_command", &RedisServer::handleClient, RedisServer::getInstance());
   // std::cout << "run rpc server on: " << 5555 << std::endl;
    server.run();
    RedisServer::getInstance()->start();
}

RedisServer::getInstance()->start();顾名思义,声明一个redis实例并启动,这部分不在本文的讨论内容之中。
因此我们先从as_sever,bind,run这三个函数入手,逐步解析butonrpc。

as_server

void buttonrpc::as_server( int port )
{
	m_role = RPC_SERVER; //设置角色为服务器
	m_socket = new zmq::socket_t(m_context, ZMQ_REP); //创建一个套接字 参数为上下文和套接字类型	 //ZMQ_REP 用于请求-应答模式
	ostringstream os;
	os << "tcp://*:" << port;
	m_socket->bind (os.str()); //绑定到指定的地址
}

as_server是初始化一个服务器,将buttonrpc作为的角色设置为服务器。其主体过程就是对zeromq对使用,因此在这之前需要对zeromq有基础的理解。关于zeromq,其链接放在参考部分。

bind

有两个bind函数,根据参数我们很容易得知调用的bind是下面这个函数

template<typename F, typename S>
inline void buttonrpc::bind(std::string name, F func, S* s) //类函数
{
	m_handlers[name] = std::bind(&buttonrpc::callproxy<F, S>, this, func, s, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}

他使用std:bind将函数存进函数映射表。我们再查看一下m_handlers的定义,如下:

    std::map<std::string, std::function<void(Serializer*, const char*, int)>> m_handlers; //函数映射表

可以看到定义的key类型为string,而value类型是std:function。
std::function是C++11引入的标准库组件,位于<functional>头文件中。它的主要作用是将可调用对象封装为一个函数对象,提供一种统一的方式来处理各种类型的可调用对象。
那么它是如何与bind对应的呢?
在std:bind中,它将buttonrpc:callproxy<F,S>绑定在this指针,而后续的func,s就是callproxy函数的前两个参数,在这里表示这两个参数的值固定。而后面的三个占位符表示还有三个不固定的参数。这样我们很容易就可以对应上。三个占位符与std:function的三个参数对应。
即Serializer*, const char*, int。
我们跳转到callproxy函数。

callproxy

template<typename F, typename S>
inline void buttonrpc::callproxy(F fun, S * s, Serializer * pr, const char * data, int len)//代理类函数
{
	callproxy_(fun, s, pr, data, len);
}

发现第一个参数F fun和第二个参数S *s对应bind的fun和s,那么后三个参数Serializer * pr, const char * data, int len,在std:bind中是三个占位符,并且和m_handlers的std:function中的三个参数完美对应。
因此我们可以了解到,在使用m_handlers时,根据name调用对应的代理函数。

callproxy_

    template<typename R, typename C, typename S>
	void callproxy_(R(C::* func)(), S* s, Serializer* pr, const char* data, int len) {
		callproxy_(std::function<R()>(std::bind(func, s)), pr, data, len);
	}

这里利用了std:function函数将func和s绑定到了一起并作为一个新的func传递给另外一个参数不同的callproxy_,我们跟着查看这个函数:

template<typename R>
void buttonrpc::callproxy_(std::function<R()> func, Serializer* pr, const char* data, int len)
{
	typename type_xx<R>::type r = call_helper<R>(std::bind(func));
	value_t<R> val;
	val.set_code(RPC_ERR_SUCCESS);
	val.set_val(r);
	(*pr) << val;
}

调用了call_helper,而call_helper的内容就是对f函数进行调用,即调用具体的对应的函数。而根据返回值也设定了返回0或者返回f函数的返回值,对应不同的处理。
文件还实现了从无参到五个参数的函数代理使用,其区别就在与参数个数和有参数时需要对参数进行序列化传递。

run

void buttonrpc::run()
{
    if (m_role != RPC_SERVER) { //如果不是服务器
		return;
	}
	while (1){
		zmq::message_t data;  //创建一个消息
		recv(data); //接收数据 没消息就阻塞
		StreamBuffer iodev((char*)data.data(), data.size());//创建一个流缓冲区
		Serializer ds(iodev); //创建一个序列化器

		std::string funname;
		ds >> funname; //读取函数名
		Serializer* r = call_(funname, ds.current(), ds.size()- funname.size()); //调用函数

		zmq::message_t retmsg (r->size()); //创建一个消息
		memcpy (retmsg.data (), r->data(), r->size()); //拷贝数据
		send(retmsg); //发送数据
		delete r;
	}

}

注释基本将函数解释完毕。这里我们思考一个redis命令的使用,例如set key value。那么从data中读取到的funname就是set。因此call传递的就是set,缓冲区当前字节流的位置current,以及参数的长度ds.size()- funname.size()。

call_

Serializer* buttonrpc::call_(std::string name, const char* data, int len)
{
    Serializer* ds = new Serializer(); //创建一个序列化器
    if (m_handlers.find(name) == m_handlers.end()) { //如果没有找到函数
		(*ds) << value_t<int>::code_type(RPC_ERR_FUNCTIION_NOT_BIND); //设置错误码
		(*ds) << value_t<int>::msg_type("function not bind: " + name); //设置错误信息
		return ds;
	}
    auto fun = m_handlers[name]; //获取函数
    fun(ds, data, len);  //调用函数
    ds->reset(); //重置序列号容器
    return ds;
}

很明显,因为我们之前存储了对应的函数在m_handlers中,这里可以根据name去查找对应的函数。如果查找到就直接进行函数调用即可。

总结

std:bind和std:function的结合使用,让对这两个c++11特性不熟悉的人会感觉代码理解很困难。但在了解这两个标准组件之后,就比较简单了。
ps:理解简单,让我来写这样的代码感觉就很困难了。

参考

zeromq

相关推荐

  1. buttonrpc解析server

    2024-07-16 19:14:01       21 阅读
  2. 远程过程调用-buttonrpc源码解析1-序列化

    2024-07-16 19:14:01       39 阅读
  3. 远程过程调用-buttonrpc源码解析6-函数调用

    2024-07-16 19:14:01       31 阅读
  4. 远程过程调用-buttonrpc源码解析8-ZMQ网络库

    2024-07-16 19:14:01       37 阅读
  5. Kubernetes api-server源码阅读3(源码

    2024-07-16 19:14:01       67 阅读
  6. 云卷云舒:【实战】Sql Server迁移

    2024-07-16 19:14:01       51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-16 19:14:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-16 19:14:01       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-16 19:14:01       58 阅读
  4. Python语言-面向对象

    2024-07-16 19:14:01       69 阅读

热门阅读

  1. Haproxy负载均衡

    2024-07-16 19:14:01       23 阅读
  2. redhat基础的环境搭建

    2024-07-16 19:14:01       22 阅读
  3. 【阶乘】个人练习-Leetcode-LCP 22. 黑白方格画

    2024-07-16 19:14:01       22 阅读
  4. EnableFeignClients详解

    2024-07-16 19:14:01       24 阅读
  5. 自动驾驶的规划控制简介

    2024-07-16 19:14:01       19 阅读
  6. 查看 RocketMQ 中的重试队列和死信队列

    2024-07-16 19:14:01       22 阅读
  7. 靖江美食元宇宙

    2024-07-16 19:14:01       20 阅读
  8. python实现自动更新prometheus规则

    2024-07-16 19:14:01       20 阅读
  9. POSIX 标准-信号量sem_t

    2024-07-16 19:14:01       19 阅读
  10. 文件访问:C/C++/MFC

    2024-07-16 19:14:01       19 阅读
  11. 常见数据库

    2024-07-16 19:14:01       22 阅读