cpp_redis是一个基于c++11编写的redis客户端,比较简单,也比较强大,支持集群
本篇分析是基于一个简单的set命令来展开的
为了便于调试和查看代码运行轨迹,编译时用如下命令:
cmake -DBUILD_EXAMPLES=true -DLOGGING_ENABLED=true .
这是查看完CMakefile.txt后得出的
cpp_redis使用了tacopie库,这个库的作者和cpp_redis是一个人,类结构层次如下:
tacopie:
tcp_client {io_service, m_socket, std::queue
io_service
{
std::unordered_map
std::thread m_poll_worker;
utils::thread_pool m_callback_workers;
{
std::vector
std::queue
}
std::vector
tacopie::self_pipe m_notifier; pipe;
std::condition_variable m_wait_for_removal_condvar;
io_service::wait_for_removal(const tcp_socket& socket)
循环等待直到socket不在m_tracked_sockets中
tcp_client::disconnect(bool wait_for_removal)如果指定wait_for_removal为true,调用
}
m_socket
tcp_socket {m_fd, m_host, m_port, m_type}
cpp_redis:
future_client {
redis_client m_client
{
redis_connection
{
tacopie::tcp_client m_client;
reply_callback_t m_reply_callback;
std::function
void redis_client::connection_receive_handler(network::redis_connection&, reply& reply)
从redis_client对象的m_callbacks中取出1个
如果用户定义了m_before_callback_handler,则执行m_before_callback_handler(reply, callback);
callback也作为参数传递给用户的回调函数,用户可选择性调callback
否则执行callback(reply);
通知等待变量
disconnection_handler_t m_disconnection_handler;
void redis_client::connection_disconnection_handler(network::redis_connection&)
清空redis_client对象的m_callbacks队列,并通知等待变量
调用用户的回调函数m_disconnection_handler(*this);
builders::reply_builder m_builder;
std::string m_buffer;
std::mutex m_buffer_mutex;
}
std::queue
std::function
//user defined disconnection handler
disconnection_handler_t m_disconnection_handler;
//user defined before callback handler
std::function
std::condition_variable m_sync_condvar;用于同步提交redis命令
}
至此,类型结构描述结束,下面是执行set命令的流程
future exec_cmd(std::function
auto prms = std::make_shared
f([prms](reply& reply) {
prms->set_value(reply);
}).commit(); f的返回值是redis_client,调用完f函数后,再调用返回的redis_client的commit方法
调用f的过程,
[=](const rcb_t& cb) -> rc& { return m_client.setex(key, seconds, value, cb); });
实际是调用m_client.setex(key, seconds, value, cb);
cb是
[prms](reply& reply) {
prms->set_value(reply);
}
m_client.setex返回redis_client对象,实际就是m_client自己
send({"SETEX", key, std::to_string(seconds), value}, reply_callback);
m_client.send(redis_cmd);
redis_connection::send
m_buffer += build_command(redis_cmd);
一条redis的命令如下,4个参数:
*4\r\n
$len(key)\r\n
key\r\n
...
m_callbacks.push(callback); cb存入队列
返回
f调用完返回redis_client,再调用其commit方法
redis_client::try_commit
redis_connection::commit
std::string buffer = std::move(m_buffer);
m_client.async_write({std::vector
tcp_client::async_write(const write_request& request)
m_io_service->set_wr_callback(m_socket, std::bind(&tcp_client::on_write_available, this, std::placeholders::_1));
设置m_socket的写回调tcp_client::on_write_available
m_notifier.notify();向管道中写入a
在io_service构造时创建了m_poll_worker线程,函数是io_service::poll
是个循环,每次循环都调用init_poll_fds_info,将m_tracked_sockets和m_fds[0]放入监听集合
m_tracked_sockets中的fd如果设置了对应的回调函数,并且当前没有在执行,则监听对应的事件
m_fds[0]监听读事件
所有这些读事件,都放在m_poll_fds_info集合
调用::poll(const_cast
如果有事件,则调用process_events(),没有事件则阻塞
m_fds[0]的读事件直接清空管道(读1024个字符),忽略
如果监听的fd返回写事件,wr_callback存在,当前没有执行,则process_wr_event(poll_result, socket);
封装一个task_t给线程池m_callback_workers执行,task_t先执行用户的回调函数wr_callback(fd);
如果该fd标记了marked_for_untrack,则从m_tracked_sockets去除,通知m_wait_for_removal_condvar.notify_all
因为task_t是异步执行的,在process_events函数里也执行了一遍marked_for_untrack,并且没有先查找
是因为process_events先拥有了m_tracked_sockets_mtx,而process_wr_event在删之前也要获得这个锁.
如果process_events当时没有删,之后又设置了,则process_wr_event可以删
process_rd_event类似于process_wr_event。
m_notifier.notify();继续驱动事件处理(为什么写在循环内,这样写了多个a)
tcp_client::on_write_available
auto callback = process_write(result);
从m_write_requests中取出一个request,m_socket.send(request.buffer, request.buffer.size());
auto callback = request.async_write_callback;
如果m_write_requests没有要发送的请求,则m_io_service->set_wr_callback(m_socket, nullptr);
即不监听写事件了
没有发送成功,则disconnect();
m_io_service->untrack(m_socket);
it->second.marked_for_untrack = true;这里设置了untrack,异步任务执行完可以删
if (wait_for_removal) { m_io_service->wait_for_removal(m_socket); } 等待fd从map中删除
默认是false
m_socket.close();
callback(result);//这里是nullptr
m_write_requests.push(request);
request存入m_write_requests队列
在connect调用时注册了tcp_client_receive_handler,即
m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)});
m_io_service->set_rd_callback(m_socket, std::bind(&tcp_client::on_read_available, this, std::placeholders::_1));
m_read_requests.push(request);
也就是响应回来,调用tcp_client::on_read_available
auto callback = process_read(result);
从m_read_requests取出一个request对象
result.buffer = m_socket.recv(request.size);
result.success = true;
如果请求队列现在空了,m_io_service->set_rd_callback(m_socket, nullptr);
调用用户定义的callback(result);即redis_connection::tcp_client_receive_handler
将request读出来的字节流作为输入给m_builder,生成多个reply。
如果构建reply失败,调用m_disconnection_handler,即redis_client::connection_disconnection_handler
clear_callbacks();清空m_callbacks队列
call_disconnection_handler()
m_disconnection_handler(*this);即用户定义的断开连接回调
对每一个reply,调用m_reply_callback(*this, reply);即redis_client::connection_receive_handler
上面已经分析过,从callback队列中得到一个回调函数,即f的参数对应的lamada函数
[prms](reply& reply) {
prms->set_value(reply);
}
m_client.async_read({__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)});
继续注册读事件
return prms->get_future();
}
}
connect流程就是设值,连接。