2012-03-31 21 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