基于boost asio的redis客户端redisclient

来源:互联网 发布:网络聊天室 编辑:程序博客网 时间:2024/05/29 19:26

如果说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<void(bool, const std::string &)> handler)
    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<void(bool, const std::string &)> &handler)
{   连接响应回来,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<size_t, RedisParser::ParseResult> result = redisParser.parse(buf.data() + pos, size - pos);
                    解析收到的数据,如果是完整的响应,装进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<typename Handler>
        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<size_t, RedisParser::ParseResult>  pair = parseArray(ptr, size);

    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<size_t, RedisParser::ParseResult>  pair = parse(ptr + position, size - position);
如果有足够的数据,每一次都能得到一个完整的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);



0 0
原创粉丝点击