當(dāng)你需要發(fā)送二進(jìn)制安全的命令可以采用%b的格式化方式,同時(shí)需要一個(gè)字符串指針和size_t類型的字符串長(zhǎng)度參數(shù),如下
reply = redisCommand(context, "SET foo %b", value, (size_t) valuelen);
在API內(nèi)部,Hiredis根據(jù)不同的參數(shù)分割命令轉(zhuǎn)化為操作redis數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)命令,你可以格式化多個(gè)參數(shù)來構(gòu)造redis的命令,如下
reply = redisCommand(context, "SET key:%s %s", myid, value);
處理redis應(yīng)答
當(dāng)命令被成功執(zhí)行后redisCommand會(huì)有相應(yīng)的返回值。如果有錯(cuò)誤發(fā)生,返回值為NULL并且redisReply結(jié)構(gòu)體中的err變量將會(huì)被設(shè)置成相應(yīng)的值(請(qǐng)參照Errors章節(jié))。一旦有錯(cuò)誤發(fā)生context不能被重用并且你需要建立一個(gè)新的連接。
redisCommand執(zhí)行后返回值類型為redisReply。通過redisReply結(jié)構(gòu)體中的type變量可以確定命令執(zhí)行的情況。
REDIS_REPLY_STATUS:
返回執(zhí)行結(jié)果為狀態(tài)的命令。比如set命令的返回值的類型是REDIS_REPLY_STATUS,然后只有當(dāng)返回信息是"OK"時(shí),才表示該命令執(zhí)行成功。可以通過reply->str得到文字信息,通過reply->len得到信息長(zhǎng)度。
REDIS_REPLY_ERROR:
返回錯(cuò)誤。錯(cuò)誤信息可以通過reply->str得到文字信息,通過reply->len得到信息長(zhǎng)度。
REDIS_REPLY_INTEGER:
返回整型標(biāo)識(shí)。可以通過reply->integer變量得到類型為long long的值。
REDIS_REPLY_NIL:
返回nil對(duì)象,說明不存在要訪問的數(shù)據(jù)。
REDIS_REPLY_STRING:
返回字符串標(biāo)識(shí)。可以通過reply->str得到具體值,通過reply->len得到信息長(zhǎng)度。
REDIS_REPLY_ARRAY:
返回?cái)?shù)據(jù)集標(biāo)識(shí)。數(shù)據(jù)集中元素的數(shù)目可以通過reply->elements獲得,每個(gè)元素是個(gè)redisReply對(duì)象,元素值可以通過reply->element[..index..].*形式獲得,用在獲取多個(gè)數(shù)據(jù)結(jié)果的操作。
執(zhí)行完命令調(diào)用后應(yīng)該通過freeReplyObject()釋放redisReply,對(duì)于嵌套對(duì)象(比如數(shù)組)要注意,并不需要嵌套進(jìn)行釋放,這樣是有害的會(huì)造成內(nèi)存破壞。
Important:hiredis當(dāng)前版本 (0.10.0)當(dāng)使用異步API時(shí)會(huì)自己釋放replies對(duì)象。這意味著你使用異步API時(shí)并不需要主動(dòng)調(diào)用freeReplyObject 。relpy對(duì)象當(dāng)回調(diào)返回時(shí)將會(huì)被自動(dòng)釋放。但是這種行為也許會(huì)在將來的版本中改變,所以升級(jí)時(shí)請(qǐng)密切關(guān)注升級(jí)日志。
清理連接資源
斷開連接并且釋放context使用以下函數(shù)
void redisFree(redisContext *c);
此函數(shù)立馬關(guān)閉socket并且釋放創(chuàng)建context時(shí)分配的資源。
發(fā)送多個(gè)命令參數(shù)
和redisCommand函數(shù)相似,redisCommandArgv函數(shù)可以用于傳輸多個(gè)命令參數(shù)。函數(shù)原型為
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
,類似于 lpush, del key1 key2..., zadd key score1 member1 score2 member2...這類命令, 其中 argc是傳遞參數(shù)的個(gè)數(shù), argv主要用于傳遞的string的value, 而argvlen 是每個(gè)string的size。
此函數(shù)返回值與redisCommand相似。參考https://gist.github.com/dspezia/1455082
管線(Pipelining)
為了搞清楚Hiredis在阻塞連接下的管線操作,需要理解其內(nèi)部執(zhí)行過程。
當(dāng)任何類似于redisCommand的函數(shù)被調(diào)用,Hiredis首先將命令格式化為redis支持的命令協(xié)議。被格式化后的命令被放入context的輸出緩沖區(qū),這個(gè)緩沖區(qū)是動(dòng)態(tài)的,所以它可以容納任意數(shù)量的命令。在命令進(jìn)入輸出緩沖區(qū)后,redisGetReply 函數(shù)被調(diào)用。這個(gè)函數(shù)有以下兩種執(zhí)行方式:
輸入緩沖區(qū)非空:
從輸入緩沖區(qū)中嘗試解析單獨(dú)的reply對(duì)象并且返回reply
如果沒有reply能被解析,執(zhí)行步驟2
輸入緩沖區(qū)為空:
將整個(gè)輸出緩沖區(qū)寫入socket
從socket中讀取數(shù)據(jù)直到有一個(gè)reply能被解析
Hiredis為了有效利用socket還提供了redisGetReply的接口。對(duì)于管線命令,需要完成的唯一事情就是填充輸出緩沖區(qū)。有兩個(gè)函數(shù)被用于執(zhí)行此操作,這兩個(gè)函數(shù)基本與redisCommand函數(shù)功能類似,但是他們不返回reply
void redisAppendCommand(redisContext *c, const char *format, ...);void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
當(dāng)這兩個(gè)函數(shù)被一次或多次調(diào)用時(shí),通過redisGetReply依次返回replies。redisGetReply的返回值要么是REDISOK或是REDISERR,REDIS_ERR意味著獲得reply發(fā)生了錯(cuò)誤,想要得到具體的錯(cuò)誤原因可以通過err變量來獲取。
以下通過一個(gè)簡(jiǎn)單例子說明管線的使用:
redisReply *reply;redisAppendCommand(context,"SET foo bar");redisAppendCommand(context,"GET foo");redisGetReply(context,&reply); // reply for SETfreeReplyObject(reply);redisGetReply(context,&reply); // reply for GETfreeReplyObject(reply);
redisGetReply這個(gè)API也可以被用來實(shí)現(xiàn)阻塞的訂閱模式
reply = redisCommand(context,"SUBSCRIBE foo");freeReplyObject(reply);while(redisGetReply(context,&reply) == REDIS_OK) {// consume messagefreeReplyObject(reply);}
Errors
如果某些函數(shù)(如redisConnect, redisCommand(調(diào)用不成功,函數(shù)返回值為NULL或者REDIS_ERR,此時(shí)context結(jié)構(gòu)體中的err成員為非零值,可能為以下幾種常量
REDIS_ERR_IO:當(dāng)創(chuàng)建連接時(shí)(試著寫socket或者讀socket)發(fā)生的I/O錯(cuò)誤。如果你在代碼中包含了errno.h頭文件,你便能得到準(zhǔn)確的錯(cuò)誤碼。
REDIS_ERR_EOF:redis服務(wù)端已經(jīng)關(guān)閉了此連接。
REDIS_ERR_PROTOCOL:服務(wù)端解析協(xié)議時(shí)發(fā)生了錯(cuò)誤。
REDIS_ERR_OTHER:其他錯(cuò)誤。目前僅僅表示無法解析目標(biāo)主機(jī)名的錯(cuò)誤。
在錯(cuò)誤情況下,可以通過context結(jié)構(gòu)體中的errstr成員得到錯(cuò)誤的確切描述。
異步API
Hiredis自帶的異步API很容易和一些基于事件的庫(kù)結(jié)合使用。比如和libev、ibevent的結(jié)合使用。
連接
函數(shù)redisAsyncConnect被用來和redis建立非阻塞連接。它返回redisAsyncContext的結(jié)構(gòu)體,結(jié)構(gòu)體的err成員用來檢查在創(chuàng)建連接的過程中是否發(fā)生了錯(cuò)誤。因?yàn)閯?chuàng)建的是非阻塞的連接,內(nèi)核并不能立馬返回一個(gè)連接指定主機(jī)的結(jié)果。
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);if (c->err) {printf("Error: %s/n", c->errstr);// handle error}
異步Context可設(shè)置一個(gè)響應(yīng)斷開連接事件的回調(diào)函數(shù),當(dāng)連接斷開時(shí)會(huì)相應(yīng)執(zhí)行。回調(diào)函數(shù)的原型為
void(const redisAsyncContext *c, int status);
在斷開連接的情況下,當(dāng)連接是由用戶自己斷開的status參數(shù)為REDISOK,如果出現(xiàn)了其他錯(cuò)誤status參數(shù)為REDISERR,當(dāng)錯(cuò)誤時(shí)通過err成員可以得到準(zhǔn)確的錯(cuò)誤碼。
當(dāng)回調(diào)執(zhí)行完畢后context對(duì)象會(huì)自己釋放資源。此事件的回調(diào)函數(shù)給你創(chuàng)建一個(gè)新連接提供了便利。
一個(gè)context對(duì)象僅能設(shè)置一次斷開連接的回調(diào),如果再進(jìn)行下一次設(shè)置將會(huì)返回REDIS_ERR。設(shè)置斷開連接回調(diào)函數(shù)的原型為:
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
發(fā)送操作命令并且響應(yīng)回調(diào)事件
在異步的情況下,redis的操作指令將會(huì)被自動(dòng)加入事件循環(huán)隊(duì)列。由于發(fā)送命令執(zhí)行的過程是異步的,當(dāng)命令執(zhí)行完畢后將會(huì)調(diào)用相應(yīng)的回調(diào)函數(shù)。回調(diào)函數(shù)的原型為
void(redisAsyncContext *c, void *reply, void *privdata);
privdata參數(shù)是由調(diào)用者自己定義的數(shù)據(jù)類型。
以下是進(jìn)行異步命令操作的函數(shù):
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,const char *format, ...);
int redisAsyncCommandArgv( redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
命令的使用方式和上面所講的同步接口類似。執(zhí)行成功返回REDIS_OK,否則返回REDIS_ERR。比如連接已經(jīng)關(guān)閉的情況下再使用redisAsyncCommand向此連接執(zhí)行命令就會(huì)返回REDIS_ERR。
回調(diào)執(zhí)行完畢后如果reply不為空,回調(diào)執(zhí)行完畢后將自動(dòng)對(duì)reply的資源進(jìn)行回收。
當(dāng)context發(fā)生錯(cuò)誤時(shí)回調(diào)得到的reply將為空。
斷開連接
一個(gè)異步的連接可以通過下面這個(gè)函數(shù)終止
void redisAsyncDisconnect(redisAsyncContext *ac);
當(dāng)這個(gè)函數(shù)被調(diào)用時(shí),連接并不會(huì)被立即關(guān)閉,而是等待所有這個(gè)連接的異步命令操作執(zhí)行完畢,并且回調(diào)事件已經(jīng)執(zhí)行完畢后才關(guān)閉此連接,這時(shí)在響應(yīng)關(guān)閉連接事件的回調(diào)函數(shù)中得到的狀態(tài)為REDIS_OK,此連接的資源也將會(huì)被自動(dòng)回收。
將其掛接到事件庫(kù)X
在context對(duì)象被創(chuàng)建后進(jìn)行很少的幾步操作就可以進(jìn)行掛接。參看目錄adapters/下是如何掛接到libev 和 libevent下的。
應(yīng)答解析API
Hiredis自帶的答復(fù)解析API ,可以很容易與更高層次的語(yǔ)言進(jìn)行綁定