EDIS_ERR;
//回复数据追加到buf中
memcpy(c->buf+c->bufpos,s,len);
c->bufpos+=len;
return REDIS_OK;
}
/**
1、如果链表长度为0: 新建一个节点并直接将robj追加到链表的尾部
2、链表长度不为0: 首先取出链表的尾部节点
1)、尾部节点的字符串长度 + robj中ptr字符串的长度 <= REDIS_REPLY_CHUNK_BYTES:
将robj->ptr追加到尾节点的tail->ptr后面
2)、反之: 新建一个节点并直接将robj追加到链表的尾部
*/
void _addReplyObjectToList(redisClient *c, robj *o) {
robj *tail;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
//链表长度为0
if (listLength(c->reply) == 0) {
incrRefCount(o);//增加引用次数
listAddNodeTail(c->reply,o);//添加到链表末尾
c->reply_bytes += zmalloc_size_sds(o->ptr); //计算o->ptr的占用内存大小
} else {
//取出链表尾中的数据
tail = listNodeva lue(listLast(c->reply));
/* Append to this object when possible. */
// 如果最后一个节点所保存的回复加上新回复内容总长度小于等于 REDIS_REPLY_CHUNK_BYTES
// 那么将新回复追加到节点回复当中。
if (tail->ptr != NULL &&
sdslen(tail->ptr)+sdslen(o->ptr) <= REDIS_REPLY_CHUNK_BYTES)
{
c->reply_bytes -= zmalloc_size_sds(tail->ptr);
tail = dupLastObjectIfNeeded(c->reply);
tail->ptr = sdscatlen(tail->ptr,o->ptr,sdslen(o->ptr));
c->reply_bytes += zmalloc_size_sds(tail->ptr);
} else {//为新回复单独创建一个节点
incrRefCount(o);
listAddNodeTail(c->reply,o);
c->reply_bytes += zmalloc_size_sds(o->ptr);
}
}
// 如果突破了客户端的最大缓存限制,那么关闭客户端
asyncCloseClientOnOutputBufferLimitReached(c);
}
sendReplyToClient函数
终于到了最后一步,把c->buf与c->reply中的数据发送给客户端即可,发送同样使用的是最原始的write函数。发送完成之后,redis将当前客户端释放,并且删除写事件,代码比较简单,不详细解释。
小结
本文粗略的介绍了Redis整体运行的流程,从服务器的角度,介绍Redis是如何初始化,创建socket,接收客户端请求,解析请求及指令的执行,反馈执行的结果集给客户端等。如果读者想更深入的了解Redis的运行机制,需要亲自阅读源码,本文可以用作参考。同时也是学习linux socket编程的好工具,原本简简单单的socket->bind->listen->accept->read->write也可以用来做许多高效的业务,是Linux socket学习的不二选择。