如果说c++11引领了C++编程的潮流,那么boost::asio则是最时尚,最fashion的设计。
redisclient基于C++11实现,它没有像cpp_redis设计自己的异步框架,而是直接使用boost asio。
redisclient的makefile写的不是很好,可能找不到boost
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/lib64)
编译
cmake -DCMAKE_BUILD_TYPE=Debug
随时调试
下面以async_set_get为例
boost::asio::io_service ioService;
redisclient::RedisAsyncClient client(ioService);
pimpl->errorHandler = std::bind(&RedisClientImpl::defaulErrorHandler, std::placeholders::_1);
默认出错处理,抛异常
Worker worker(ioService, client);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(address), port);
client.asyncConnect(endpoint, std::bind(&Worker::onConnect, &worker,
std::placeholders::_1, std::placeholders::_2));
ioService.run();
RedisAsyncClient::asyncConnect调用RedisAsyncClient::connect
void RedisAsyncClient::connect(const boost::asio::ip::tcp::endpoint &endpoint,
std::function
if( pimpl->state == State::Unconnected || pimpl->state == State::Closed )
{
pimpl->state = State::Connecting;
boost的socket
pimpl->socket.async_connect(endpoint, std::bind(&RedisClientImpl::handleAsyncConnect,
pimpl, std::placeholders::_1, std::move(handler))); 指定第二个参数
}
else
{
std::stringstream ss;
ss << "RedisAsyncClient::connect called on socket with state " << to_string(pimpl->state);
handler(false, ss.str());
}
void RedisClientImpl::handleAsyncConnect(const boost::system::error_code &ec,
const std::function
{ 连接响应回来,handler是Worker::onConnect
if( !ec )
{
socket.set_option(boost::asio::ip::tcp::no_delay(true));
state = State::Connected;
handler(true, std::string()); 调用Worker::onConnect
processMessage();
socket.async_read_some(boost::asio::buffer(buf), socket可读了(connect不会有数据回来),数据读到buf中,调用回调函数
std::bind(&RedisClientImpl::asyncRead, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
std::pair
解析收到的数据,如果是完整的响应,装进valueStack,doProcessMessage(std::move(redisParser.result()));继续读;
handlers.front()(std::move(v)); //Worker::onSet
handlers.pop();
如果不完整,调用processMessage,返回(当有数据时,这个函数还会被驱动)
如果数据都解析完了,调用processMessage,返回
}
else
{
state = State::Unconnected;
handler(false, ec.message());
}
}
void Worker::onConnect(bool connected, const std::string &errorMessage)
redisClient.command("AUTH", {"123456"});
redisClient.command("SET", {redisKey, redisValue}, std::bind(&Worker::onSet, this, std::placeholders::_1));
args.emplace_front(cmd);
pimpl->post(std::bind(&RedisClientImpl::doAsyncCommand, pimpl,
std::move(pimpl->makeCommand(args)), std::move(handler)));
template
inline void RedisClientImpl::post(const Handler &handler)
{
strand.post(handler); //boost::asio::strand strand(ioService)
}
提交一个函数执行
pimpl的RedisClientImpl::doAsyncCommand,参数是pimpl->makeCommand(args)和handler
handlers.push( std::move(handler));
dataQueued.push_back(std::move(buff));
如果上次写的数据还未清空,即还没写完,先返回;
asyncWrite(boost::system::error_code(), 0);
dataWrited.clear();清空已写数据
boost::asio::async_write(socket, buffers,
std::bind(&RedisClientImpl::asyncWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
socket可写,就会调用这个函数,写完还会调用asyncWrite,直到dataQueued的数据被写完
RedisParser::parseArray分析:
arrayStack每一项代表一个数组的长度,有多少项,就是有多少个数组
valueStack每一项表示一个数组,这个数组在解析时取出来,valueStack上面的空间可以作为解析元素来使用
支持多维数组
if( arrayStack.empty() == false )出现这种情况是因为,上次解析某个数组元素时不完整,依次保存了该元素的父亲数组,祖父数组,
等等数组的剩余大小,越远古的数组保存在约靠近栈顶方向,这次有数据了,调用parseArray,肯定要先解析最内层的数组
所以这里用了递归,如果还不完整,依次返回。如果解析除了一个完整的包,则加进对应的valueStack,--arraySize
如果arraySize减为0,则这个数组就解析完了,返回上一层
{
std::pair
if( pair.second != Completed )
{
valueStack.push(std::move(arrayValue));
arrayStack.push(arraySize);
return pair;
}
else
{
arrayValue.push_back( std::move(valueStack.top()) );
valueStack.pop();
--arraySize;
}
position += pair.first;
}
if( position == size )
{没有数据了
valueStack.push(std::move(arrayValue));
if( arraySize == 0 )
{ 解析完这一层数组
return std::make_pair(position, Completed);
}
else
{还要解析
arrayStack.push(arraySize);
return std::make_pair(position, Incompleted);
}
}
这是正常的解析流程
long int arrayIndex = 0;
for(; arrayIndex < arraySize; ++arrayIndex)
{
std::pair
如果有足够的数据,每一次都能得到一个完整的value,
arrayValue.push_back( std::move(valueStack.top()) );
valueStack.pop();
最后valueStack.push(std::move(arrayValue))
如果数据不足,则保存现场,下次再解析
arraySize -= arrayIndex;
valueStack.push(std::move(arrayValue));
arrayStack.push(arraySize);