使用libevent进行异步Redis池化

我想尽可能多地从Redis + Hiredis + libevent获得。

我正在使用以下代码(没有任何检查是简短的)

#include  #include  #include  #include  #include  #include  #include  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什么时候需要做redisAsyncConnect ? 在每个回调中的main()一次或(如示例所示)。 我能做些什么来提升性能吗?

我得到大约6000-7000 req / s。 使用ab对此进行基准测试,在尝试使用大数字(例如10k reqs)时,事情变得复杂 – 它无法完成基准测试和冻结。 做同样的事情但阻塞方式结果是5000-6000 req / s。

我已经通过limit -n 10000扩展了最大文件。 我正在使用Mac OS X Lion。

当然,打开Redis连接一次会更好,并尝试尽可能地重用它。

使用提供的程序,我怀疑基准冻结,因为短暂端口范围内的空闲端口数量已经耗尽。 每次打开和关闭与Redis的新连接时,相应的套接字都会在TIME_WAIT模式下花费一些时间(可以使用netstat命令检查此点)。 内核无法足够快地回收它们。 如果它们太多,则无法启动其他客户端连接。

您在程序中也有内存泄漏:为每个请求分配reqData结构,并且永远不会释放。 get_cb中缺少免费。

实际上,有两种可能的TIME_WAIT套接字源:用于Redis的套接字,以及由基准工具打开以连接到服务器的套接字。 Redis连接应该在程序中分解。 必须将基准测试工具配置为使用HTTP 1.1和keepalived连接。

就个人而言,我更喜欢用ab 围攻 ab来运行这种基准。 对于对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连接,并自然地从流水线操作中受益。 源代码可在此处获得 。 这就是我们得到的原因:

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

换句话说,通过删除系统连接/断开事件,我们可以实现两倍的吞吐量。 Redis调用的性能并不比没有Redis调用的性能差。

要进一步优化,您可以考虑在服务器和Redis之间使用unix域套接字,和/或汇集动态分配的对象以减少CPU消耗。

更新:

要试用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 

然后替换连接function:

 c = redisAsyncConnect("0.0.0.0", 6379); 

通过:

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

注意:在这里,hiredis async在管道命令方面做得很好(假设连接是永久性的),因此影响很小。