我想从Redis + Hiredis + libevent中获得尽可能多的收益。
我正在使用以下代码(没有简短的说明)
#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。
gcc -o main -levent -lhiredis main.c
我好奇何时需要做redisAsyncConnect?在main()每个回调中一次或(如示例所示)。我有什么办法可以提高性能?
redisAsyncConnect
main()
我得到大约6000-7000 req / s。使用ab到这个基准,复杂化的东西当试图大的数字(如10K请求数) -它无法完成的基准和冻结。做同样的事情但阻塞的结果是5000-6000 req / s。
ab
我已将最大文件扩展为limit -n 10000。我正在使用Mac OS X Lion。
limit -n 10000
当然,最好一次打开Redis连接,并尝试尽可能地重用它。
使用提供的程序,我怀疑基准会冻结,因为临时端口范围内的可用端口数已用尽。每次打开和关闭与Redis的新连接时,相应的套接字都会在TIME_WAIT模式下花费一些时间(可以使用netstat命令检查这一点)。内核无法足够快地回收它们。如果它们太多,则无法启动进一步的客户端连接。
您的程序中还会发生内存泄漏:reqData结构是为每个请求分配的,永远不会释放。免费缺少get_cb。
实际上,TIME_WAIT套接字有两种可能的来源:用于Redis的来源,以及由基准测试工具打开以连接到服务器的来源。Redis连接应在程序中进行分解。必须将基准测试工具配置为使用HTTP 1.1和保持连接。
就个人而言,我更喜欢使用围攻而不是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
然后替换连接功能:
c = redisAsyncConnect("0.0.0.0", 6379);
通过:
c = redisAsyncConnectUnix("/tmp/redis.sock");
注意:此处,hiredis async在流水命令方面做得很好(假设连接是永久的),因此影响将很小。