2012-03-31 22 views
4

我想盡可能地從Redis + Hiredis + libevent獲得。使用libevent的異步Redis池合

我使用下面的代碼(沒有任何檢查以短)

#include <stdlib.h> 
#include <event2/event.h> 
#include <event2/http.h> 
#include <event2/buffer.h> 
#include <hiredis/hiredis.h> 
#include <hiredis/async.h> 
#include <hiredis/adapters/libevent.h> 

typedef struct reqData { 
    struct evhttp_request* req; 
    struct evbuffer* buf; 
} reqData; 

struct event_base* base; 
redisAsyncContext* c; 

void get_cb(redisAsyncContext* context, void* r, void* data) { 
    redisReply* reply = r; 
    struct reqData* rd = data; 

    evbuffer_add_printf(rd->buf, "%s", reply->str); 
    evhttp_send_reply(rd->req, HTTP_OK, NULL, rd->buf); 

    evbuffer_free(rd->buf); 
    redisAsyncDisconnect(context); 
} 

void cb(struct evhttp_request* req, void* args) { 
    struct evbuffer* buf; 
    buf = evbuffer_new(); 

    reqData* rd = malloc(sizeof(reqData)); 
    rd->req = req; 
    rd->buf = buf; 

    c = redisAsyncConnect("0.0.0.0", 6380); 
    redisLibeventAttach(c, base); 

    redisAsyncCommand(c, get_cb, rd, "GET name"); 
} 

int main(int argc, char** argv) { 
    struct evhttp* http; 
    struct evhttp_bound_socket* sock; 

    base = event_base_new(); 
    http = evhttp_new(base); 
    sock = evhttp_bind_socket_with_handle(http, "0.0.0.0", 8080); 

    evhttp_set_gencb(http, cb, NULL); 

    event_base_dispatch(base); 

    evhttp_free(http); 
    event_base_free(base); 
    return 0; 
} 

要編譯,使用gcc -o main -levent -lhiredis main.c假設的libevent,redis的和hiredis在系統。

我很好奇當我需要做redisAsyncConnect?在main()曾經或(如示例所示)在每個回調中。我能做些什麼來提升表現嗎?

我得到約6000-7000請求/秒。使用ab作爲基準,當嘗試大數字時(例如10k reqs),東西變得複雜 - 它不能完成基準和凍結。做同樣的事情,但阻止結果是5000-6000請求/秒。

我已將最大文件擴展爲limit -n 10000。我正在使用Mac OS X Lion。

回答

2

當然要好一點,打開Redis連接一次,並儘可能地重用它。

由於提供的程序,我懷疑基準會凍結,因爲臨時端口範圍內的空閒端口數量已耗盡。每次打開和關閉與Redis的新連接時,對應的套接字都會花費TIME_WAIT模式一段時間(可使用netstat命令檢查此點)。內核無法足夠快地回收它們。當你有太多的人時,不能啓動更多的客戶端連接。

程序中還有一個內存泄漏:reqData結構被分配給每個請求,並且從未被釋放。 get_cb中缺少一個空閒空間。

實際上,有兩種可能的TIME_WAIT套接字來源:用於Redis的套接字和由基準測試工具打開以連接到服務器的套接字。 Redis連接應該在程序中分解。基準工具必須配置爲使用HTTP 1.1和Keepalived連接。

就個人而言,我更喜歡在ab上使用siege來運行這種基準。大多數對HTTP服務器進行基準測試的人都認爲ab是一種天真的工具。

在我的舊的Linux PC,初步方案,對圍困在基準標記模式與50個的keepalived連接運行,結果:

Transaction rate:   3412.44 trans/sec 
Throughput:      0.02 MB/sec 

當我們徹底刪除調用Redis的,只返回一個空的結果,我們得到:

Transaction rate:   7417.17 trans/sec 
Throughput:      0.04 MB/sec 

現在,讓我們修改程序以分解Redis連接,並自然受益於流水線。源代碼可用here。這是爲什麼我們得到:

Transaction rate:   7029.59 trans/sec 
Throughput:      0.03 MB/sec 

換句話說,通過去除系統的連接/斷開事件,就可以達到兩倍的吞吐量。 Redis呼叫的性能不會比我們在沒有任何Redis呼叫的情況下獲得的性能 。

爲了進一步優化,您可以考慮在您的服務器和Redis之間使用unix域套接字,和/或彙集動態分配的對象以減少CPU消耗。

UPDATE:

要試驗一個Unix域套接字,它很簡單:你只需要通過更新配置文件來激活的Redis本身的支持:

# Specify the path for the unix socket that will be used to listen for 
# incoming connections. There is no default, so Redis will not listen 
# on a unix socket when not specified. 
# 
unixsocket /tmp/redis.sock 
unixsocketperm 755 

,然後更換連接功能:

c = redisAsyncConnect("0.0.0.0", 6379); 

由:

c = redisAsyncConnectUnix("/tmp/redis.sock"); 

注意:這裏,hiredis async在管道命令(如果連接是永久的)方面做得很好,所以影響會很小。

+0

無法想象更好,更有價值的答案。很多,非常感謝Didier!你也可以告訴我更多關於進一步優化域套接字嗎?或者將我鏈接到一些資源? – 2012-04-01 13:04:37

+0

我相應地更新了我以前的答案。 – 2012-04-01 18:49:12

+0

謝謝!迫不及待地檢查基準。 – 2012-04-01 19:01:04