当前位置: 首页 > 面试题库 >

使用libevent的异步Redis池

李俊雅
2023-03-14
问题内容

我想从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。

我好奇何时需要做redisAsyncConnect?在main()每个回调中一次或(如示例所示)。我有什么办法可以提高性能

我得到大约6000-7000 req / s。使用ab到这个基准,复杂化的东西当试图大的数字(如10K请求数)
-它无法完成的基准和冻结。做同样的事情但阻塞的结果是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和保持连接。

就个人而言,我更喜欢使用围攻而不是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在流水命令方面做得很好(假设连接是永久的),因此影响将很小。



 类似资料:
  • 问题内容: 在我的应用程序(node / express / redis)中,我使用一些代码同时更新数据库中的多个项目: 我可以确定在方法返回之前将执行所有这些操作吗?我关心的是异步处理。由于我不在db操作中使用回调函数,因此可以吗? 问题答案: 使用MULTI / EXEC命令创建命令队列并连续执行它们。然后使用回调发送回连贯的响应(成功/失败)。请注意,您必须使用Redis的AOF来避免这种情

  • 异步队列区别于 RabbitMQ Kafka 等消息队列,它只提供一种 异步处理 和 异步延时处理 的能力,并 不能 严格地保证消息的持久化和 不支持 完备的 ACK 应答机制。 安装 composer require hyperf/async-queue 配置 配置文件位于 config/autoload/async_queue.php,如文件不存在可自行创建。 暂时只支持 Redis Dri

  • 异步Redis客户端 连接池(连接池默认开启) use AsyncRedis; //关闭连接池 AsyncRedis::enablePool(false); //开启连接池 AsyncRedis::enablePool(true); 使用AsyncRedis use AsyncRedis; //设置超时时间 AsyncRedis::s

  • (要求Workerman版本>=3.3.6) 安装: composer require clue/redis-react 示例: <?php require_once __DIR__ . '/vendor/autoload.php'; use ClueReactRedisFactory; use ClueReactRedisClient; use WorkermanWorker; $worker

  • 问题内容: 如何将以下同步伪代码转换为异步js代码 在同步编程中,这似乎很自然: 得到一些ID 通过ID获取物品 获取每个项目的相关信息,并将这些信息附加到它们 将所有项目合并到一个数组中并返回 我尝试使用MULTI,但是当递归深入时,它似乎不起作用。 有没有建议学习异步范例编程?(最好是用js代码而不是.net的东西) 问题答案: 对于异步范例中的编程学习有什么建议吗? 尝试看看这些: 了解事件