前一篇介绍了redis处理请求的过程,接下来是如何发送响应内容。
在请求处理完之后,进行响应时,需要调用addReplyXXX族函数,具体包括:
void addReply(redisClient *c, robj *obj)
void addReplySds(redisClient *c, sds s)
void addReplyString(redisClient *c, char *s, size_t len)
这几个函数又会被封装成addReplyBulk等函数。这里以addReply为例进行分析。
首先,先介绍一下北京知识。redis将响应内容组织成两部分,一个固定大小的buffer(16KB),一个响应内容对象的链表。在链表为空并且buffer有足够空间时,则将响应添加到buffer中,否则创建一个节点追加到链表上。固定buffer和响应链表,整体上构成了一个队列。这么组织的好处是,既可以节省内存(不需一开始预先分配大块内存,动态内存的由链表cover),并且可以避免频繁分配、回收内存(在响应内容小于buffer大小时)。
在addReply函数中,首先会调用prepareClientToWrite,在响应前进行初始化工作。
if (prepareClientToWrite(c) != REDIS_OK) return;
prepareClientToWrite函数在每次发送响应时调用,并且要在向响应buffer添加数据之前。只有在返回REDIS_OK时,才能向响应buffer输出内容。在响应buffer为空时(固定buffer以及链表均为空)时,向事件循环注册写事件,回调函数是sendReplyToClient。
/* This function is called every time we are going to transmit new data
* to the client. The behavior is the following:
*
* If the client should receive new data (normal clients will) the function
* returns REDIS_OK, and make sure to install the write handler in our event
* loop so that when the socket is writable new data gets written.
*
* If the client should not receive new data, because it is a fake client,
* a master, a slave not yet online, or because the setup of the write handler
* failed, the function returns REDIS_ERR.
*
* Typically gets called every time a reply is built, before adding more
* data to the clients output buffers. If the function returns REDIS_ERR no
* data should be appended to the output buffers. */
int prepareClientToWrite(redisClient *c) {
if (c->flags & REDIS_LUA_CLIENT) return REDIS_OK;
if ((c->flags & REDIS_MASTER) &&
!(c->flags & REDIS_MASTER_FORCE_REPLY)) return REDIS_ERR;
if (c->fd <= 0) return REDIS_ERR; /* Fake client */
// <MM>
// 在没有向output buf输出之前,才可以注册write event hand