redis 異步api


hiredis中異步的實現小結

前言

一般情況下我們使用的都是hiredis的同步通信機制,這種機制下每當你向服務器發送命令請求,程序都會阻塞直到收到服務器的回復並處理。而 如果采用異步通信,程序就不需要阻塞等待服務器的回復,而是直接繼續執行后邊的代碼,當服務器回復到來后由程序中預先注冊的回調函數來處理回復。

同步通信下程序寫起來邏輯更清晰,代碼量也少,但是由於每次請求都要停下來等待回復,可能會影響程序的運行速度。異步通信下程序邏輯會變得很復 雜,你必須考慮回調函數的編寫,並且需要多開一個線程來實現異步事件的處理,但是異步通信下程序在發送完redis命令請求后不需要等待回復,可以繼續做 其他事,程序速度的提升自然不言而喻。異步通信比較適合程序對速度要求比較高的情況下。

hiredis中的異步api

hiredis中有一套異步api可供我們使用。要使用hiredis中的異步api你必須先了解hiredis中的異步實現。hiredis 的異步主要是通過libevent等異步事件觸發庫來實現的。hiredis可以通過一下事件觸發庫:libae(redis自帶的異步事件觸發庫)、 libev、libuv、libevent中的一個實現。在我的實際使用中,libae庫出現了頭文件問題,libev出現了異步消息無法接受的問 題,libuv沒有安裝成功,所有我最終選擇了libevent庫,而這個庫的表現也非常穩定。

要使用redis客戶端的異步通信,單靠hiredis自帶的那幾個api是不夠的,還需要事件觸發庫的支持 。這里要黑一下hiredis的github主頁,上邊的異步api說明中沒有講解hiredis異步api所需的那些事件觸發庫,讓我想當然的以為單單 依靠hiredis的自帶api就可以實現異步,結果浪費了大量時間去調試錯誤的程序,希望大家引以為戒。下邊就以libevent為例講一下 hiredis異步api用到的事件觸發庫。

事件觸發庫libevent

libevent是一個成熟事件觸發庫,分布式緩存軟件memcached就使用了這個庫。libevent可以實現對多種事件的觸發管理。詳 細的說,你可以通過libevent去對各種IO事件進行觸發注冊,之后如果該IO事件發生,libevent就會直接調用你之前為IO事件注冊的函數來 處理這個事件。除了IO事件外,libevent還可以管理定時器事件、信號事件,功能非常的強大。

下邊簡單講一下libevent的使用。libevent本身的使用是比較復雜的,考慮到我們的重點是hiredis,所以這里只講hiredis中要用到的。libevent首先要設置並添加你要監聽的異步事件,這一步hiredis已經為你做好了,只需要兩步:

base = event_base_new();//創建一個新的libevent事件處理實例。

redisLibeventAttach(ac, base);//對hiredis進行libevent事件注冊,這里的base就是上一步創建的事件處理實例

event_base_dispatch(base);//開始libevent事件處理循環。運行這個函數后libevent才真正開始監聽並處理事件。在實際的hiredis使用中這個函數通常要單獨開一個線程去運行,因為這個函數運行后就會陷入死循環。

hiredis用到的libevent函數就這么幾個,是不是覺得很簡單!

hiredis異步APi的使用

下邊才是重點,如何使用hiredis的異步API來實現我們要做的redi異步通信。

首先要創建連接:

redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);

這里的創建連接跟同步下區別不大。但是需要注意的是異步的連接函數會立刻返回,不論你的程序是否真的連上了redis服務器。是否成功連接只能在連接回調函數中確定。所以不要指望依靠這個函數去檢查你的連接是否成功建立。

可以通過這個函數注冊連接回調函數:

redisAsyncSetConnectCallback(c, ccdbRedisAsync::connectCallback);

回調函數需要是下邊的格式:

void ccdbRedisAsync::connectCallback(const redisAsyncContext *c, int status)

其中參數status會告訴你連接是否成功。

以下函數實現斷開連接以及相應的回調函數:

redisAsyncDisconnect(c);//斷開連接

redisAsyncSetDisconnectCallback(c, ccdbRedisAsync::disconnectCallback);//回調函數的格式和使用同連接回調函數

如果你想實現redis的斷線重連,那么就可以考慮在上邊的回調函數中實現。

注意創建連接后還要進行之前的libevent事件注冊過程。

連接創建好后解可以發送命令了。異步命令的發送方式和同步很像,區別在於異步發送函數執行后只能得到該命令是否成功過加入發送隊列的返回,而無法確定這個命令是否發送成功以及命令的返回。

int redisAsyncCommand(

redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);//參數fn是回調函數的地址,privdate可以用來存儲             任意的用戶指針,這個指針可以在回調函數調用的時候得到

一般你需要設置一個回調函數來處理命令的返回:

void(redisAsyncContext *c, void *reply, void *privdata);

其中的reply參數指向的是與同步下有相同定義的reply結構。注意此處的reply占用的空間是會在回調函數執行后被自動釋放的,這點要區別於同步。private參數是你發送命令時所指定的指針,你可以把一些信息,例如所執行的命令保存在這個指針的空間中,這樣回調函數被調用的時候你才能判斷這個回復是由之前執行的哪個命令產生的。

上邊就是異步redis所需的主要API了。

例程

下邊的例程來自hiredis的作者。注意這個歷程里邊沒有為libevent事件處理單開線程,這在實際運用中是不多見的。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <signal.h>#include <hiredis.h>#include <async.h>#include <adapters/libevent.h>//設置命令執行后的回調函數void getCallback(redisAsyncContext *c, void *r, void *privdata) { redisReply *reply = r; if (reply == NULL) return; printf("argv[%s]: %s\n", (char*)privdata, reply->str); /* Disconnect after receiving the reply to GET */redisAsyncDisconnect(c); } //設置連接回調函數void connectCallback(const redisAsyncContext *c, int status) { if (status != REDIS_OK) { printf("Error: %s\n", c->errstr); return; } printf("Connected...\n"); } //設置斷開連接回調函數void disconnectCallback(const redisAsyncContext *c, int status) { if (status != REDIS_OK) { printf("Error: %s\n", c->errstr); return; } printf("Disconnected...\n"); } int main (int argc, char **argv) { signal(SIGPIPE, SIG_IGN);//捕捉程序收到數據包時候的信號struct event_base *base = event_base_new();//新建一個libevent事件處理redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);//新建異步連接if (c->err) { /* Let *c leak for now... */printf("Error: %s\n", c->errstr); return1; } redisLibeventAttach(c,base);//將連接添加到libevent事件處理redisAsyncSetConnectCallback(c,connectCallback);//設置連接回調redisAsyncSetDisconnectCallback(c,disconnectCallback);//設置斷開連接回調redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));//發送set命令redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");//發送get命令event_base_dispatch(base);//開始libevent循環。注意在這一步之前redis是不會進行連接的,前邊調用的命令發送函數也沒有真正發送命令return0; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM