初始化
redisContext - Redis連接的上下文
/* Context for a connection to Redis */ typedef struct redisContext { int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ int fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */ enum redisConnectionType connection_type; struct timeval *timeout; struct { char *host; char *source_addr; int port; } tcp; struct { char *path; } unix_sock; } redisContext;
每一次連接成功, 都會創建一個redisContext數據, 用於后續的操作
redisReader - 結果讀取
typedef struct redisReader { int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ char *buf; /* Read buffer */ size_t pos; /* Buffer cursor */ size_t len; /* Buffer length */ size_t maxbuf; /* Max length of unused buffer */ redisReadTask rstack[9]; int ridx; /* Index of current read task */ void *reply; /* Temporary reply pointer */ redisReplyObjectFunctions *fn; void *privdata; } redisReader;
redisReader在redisContext中使用redisReplyObjectFunctions參數進行初始化, 用於讀取執行結果, 其中 redisReplyObjectFunctions 是一個函數指針列表, 用於讀取不同類型的返回數據,
typedef struct redisReplyObjectFunctions { void *(*createString)(const redisReadTask*, char*, size_t); void *(*createArray)(const redisReadTask*, int); void *(*createInteger)(const redisReadTask*, long long); void *(*createNil)(const redisReadTask*); void (*freeObject)(void*); } redisReplyObjectFunctions;
一般使用的是默認的defaultFunctions進行初始化
/* Default set of functions to build the reply. Keep in mind that such a * function returning NULL is interpreted as OOM. */ static redisReplyObjectFunctions defaultFunctions = { createStringObject, createArrayObject, createIntegerObject, createNilObject, freeReplyObject };
redisReader中的另一個結構體是 redisReadTask數組, 需要配合ridx一起使用, 用於給redisReplyObjectFunctions中的函數提供輸入參數
typedef struct redisReadTask { int type; int elements; /* number of elements in multibulk container */ int idx; /* index in parent (array) object */ void *obj; /* holds user-generated value for a read task */ struct redisReadTask *parent; /* parent task */ void *privdata; /* user-settable arbitrary field */ } redisReadTask;
在defaultFunctions下面的函數是 createStringObject, createArrayObject, createIntegerObject, createNilObject, freeReplyObject, 這些函數的輸入都是rediskReadTask, 但是根據類型不同會有不同的附帶參數, 例如array就會有elements元素數量, 而string就會有char *str和size_t len, 都會在處理中產生redisReply對象, 並將這個對象掛載到上一級redisReply對象的element變量上(如果有上一級的話).
結果解析
執行命令時, 實際調用的處理方法是redisvCommand,
void *redisvCommand(redisContext *c, const char *format, va_list ap) { if (redisvAppendCommand(c,format,ap) != REDIS_OK) return NULL; return __redisBlockForReply(c); }
在這里會調用__redisBlockForReply來處理存在redisContext *c中的結果
/* Helper function for the redisCommand* family of functions. * * Write a formatted command to the output buffer. If the given context is * blocking, immediately read the reply into the "reply" pointer. When the * context is non-blocking, the "reply" pointer will not be used and the * command is simply appended to the write buffer. * * Returns the reply when a reply was successfully retrieved. Returns NULL * otherwise. When NULL is returned in a blocking context, the error field * in the context will be set. */ static void *__redisBlockForReply(redisContext *c) { void *reply; if (c->flags & REDIS_BLOCK) { if (redisGetReply(c,&reply) != REDIS_OK) return NULL; return reply; } return NULL; }
這里會將錯誤情況排除, 實際的處理方法是 redisGetReply, 這里的處理方式是阻塞的, 如果aux已經讀到error就返回, 否則一直在等直到有值
int redisGetReply(redisContext *c, void **reply) { int wdone = 0; void *aux = NULL; /* Try to read pending replies */ if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ if (aux == NULL && c->flags & REDIS_BLOCK) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) return REDIS_ERR; } while (!wdone); /* Read until there is a reply */ do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } /* Set reply object */ if (reply != NULL) *reply = aux; return REDIS_OK; }
在阻塞讀取的過程中, 會先通過redisBufferRead嘗試讀取, 成功后再用redisGetReplyFromReader嘗試解析
/* Use this function to handle a read event on the descriptor. It will try * and read some bytes from the socket and feed them to the reply parser. * * After this function is called, you may use redisContextReadReply to * see if there is a reply available. */ int redisBufferRead(redisContext *c) { char buf[1024*16]; int nread; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; nread = read(c->fd,buf,sizeof(buf)); if (nread == -1) { if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { /* Try again later */ } else { __redisSetError(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } } else if (nread == 0) { __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); return REDIS_ERR; } else { if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } } return REDIS_OK; }
在redisGetReplyFromReader中調用redisReaderGetReply解析, 再往內部, 就是循環調用processItem來處理結果了
/* Internal helper function to try and get a reply from the reader, * or set an error in the context otherwise. */ int redisGetReplyFromReader(redisContext *c, void **reply) { if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } return REDIS_OK; } int redisReaderGetReply(redisReader *r, void **reply) { /* Default target pointer to NULL. */ if (reply != NULL) *reply = NULL; /* Return early when this reader is in an erroneous state. */ if (r->err) return REDIS_ERR; /* When the buffer is empty, there will never be a reply. */ if (r->len == 0) return REDIS_OK; /* Set first item to process when the stack is empty. */ if (r->ridx == -1) { r->rstack[0].type = -1; r->rstack[0].elements = -1; r->rstack[0].idx = -1; r->rstack[0].obj = NULL; r->rstack[0].parent = NULL; r->rstack[0].privdata = r->privdata; r->ridx = 0; } /* Process items in reply. */ while (r->ridx >= 0) if (processItem(r) != REDIS_OK) break; /* Return ASAP when an error occurred. */ if (r->err) return REDIS_ERR; /* Discard part of the buffer when we've consumed at least 1k, to avoid * doing unnecessary calls to memmove() in sds.c. */ if (r->pos >= 1024) { sdsrange(r->buf,r->pos,-1); r->pos = 0; r->len = sdslen(r->buf); } /* Emit a reply when there is one. */ if (r->ridx == -1) { if (reply != NULL) *reply = r->reply; r->reply = NULL; } return REDIS_OK; }
在processItem中, 會根據第一個字節確定當前redisReadTask的類型, 然后對類型為integer, string和array的數據進行處理, 其他的直接返回error
static int processItem(redisReader *r) { redisReadTask *cur = &(r->rstack[r->ridx]); char *p; /* check if we need to read type */ if (cur->type < 0) { if ((p = readBytes(r,1)) != NULL) { switch (p[0]) { case '-': cur->type = REDIS_REPLY_ERROR; break; case '+': cur->type = REDIS_REPLY_STATUS; break; case ':': cur->type = REDIS_REPLY_INTEGER; break; case '$': cur->type = REDIS_REPLY_STRING; break; case '*': cur->type = REDIS_REPLY_ARRAY; break; default: __redisReaderSetErrorProtocolByte(r,*p); return REDIS_ERR; } } else { /* could not consume 1 byte */ return REDIS_ERR; } } /* process typed item */ switch(cur->type) { case REDIS_REPLY_ERROR: case REDIS_REPLY_STATUS: case REDIS_REPLY_INTEGER: return processLineItem(r); case REDIS_REPLY_STRING: return processBulkItem(r); case REDIS_REPLY_ARRAY: return processMultiBulkItem(r); default: assert(NULL); return REDIS_ERR; /* Avoid warning. */ } }
對redisReadTask隊列和ridx的操作只發生在類型為array的情況下, 是在processMultiBulkItem的處理中進行的, 這里會取出一整個這一層array里的數據, 如果遇到元素的elements大於0的, 說明這個元素也是一個數組, 那么就會ridx加一, 在rstack中增加一個redisReadTask, 這時候就不再繼續處理同層數據了, 而是等着外循環去進入下一層處理.
static int processMultiBulkItem(redisReader *r) { redisReadTask *cur = &(r->rstack[r->ridx]); void *obj; char *p; long long elements; int root = 0, len; /* Set error for nested multi bulks with depth > 7 */ if (r->ridx == 8) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "No support for nested multi bulk replies with depth > 7"); return REDIS_ERR; } if ((p = readLine(r,&len)) != NULL) { if (string2ll(p, len, &elements) == REDIS_ERR) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Bad multi-bulk length"); return REDIS_ERR; } root = (r->ridx == 0); if (elements < -1 || elements > INT_MAX) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Multi-bulk length out of range"); return REDIS_ERR; } if (elements == -1) { if (r->fn && r->fn->createNil) obj = r->fn->createNil(cur); else obj = (void*)REDIS_REPLY_NIL; if (obj == NULL) { __redisReaderSetErrorOOM(r); return REDIS_ERR; } moveToNextTask(r); } else { if (r->fn && r->fn->createArray) obj = r->fn->createArray(cur,elements); else obj = (void*)REDIS_REPLY_ARRAY; if (obj == NULL) { __redisReaderSetErrorOOM(r); return REDIS_ERR; } /* Modify task stack when there are more than 0 elements. */ if (elements > 0) { cur->elements = elements; cur->obj = obj; r->ridx++; r->rstack[r->ridx].type = -1; r->rstack[r->ridx].elements = -1; r->rstack[r->ridx].idx = 0; r->rstack[r->ridx].obj = NULL; r->rstack[r->ridx].parent = cur; r->rstack[r->ridx].privdata = r->privdata; } else { moveToNextTask(r); } } /* Set reply if this is the root object. */ if (root) r->reply = obj; return REDIS_OK; } return REDIS_ERR; }
這里要注意的是moveToNextTask這個函數, 這是去移動到當前層的下一個元素, 如果當前層已經空了就直接返回, 如果在當前層的編號已經到最后一個, 就返回上一層. 所以這個hiredis對返回數組的處理是一個深度遍歷, 並且規定了層數不能超過8
static void moveToNextTask(redisReader *r) { redisReadTask *cur, *prv; while (r->ridx >= 0) { /* Return a.s.a.p. when the stack is now empty. */ if (r->ridx == 0) { r->ridx--; return; } cur = &(r->rstack[r->ridx]); prv = &(r->rstack[r->ridx-1]); assert(prv->type == REDIS_REPLY_ARRAY); if (cur->idx == prv->elements-1) { r->ridx--; } else { /* Reset the type because the next item can be anything */ assert(cur->idx < prv->elements); cur->type = -1; cur->elements = -1; cur->idx++; return; } } }
.