Hiredis: 为发布/订阅添加代码示例

创建于 2011-07-15  ·  17评论  ·  资料来源: redis/hiredis

最有用的评论

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis/hiredis.h"
#include "hiredis/async.h"
#include "hiredis/adapters/libevent.h"

void onMessage(redisAsyncContext *c, void *reply, void *privdata) {
    redisReply *r = reply;
    if (reply == NULL) return;

    if (r->type == REDIS_REPLY_ARRAY) {
        for (int j = 0; j < r->elements; j++) {
            printf("%u) %s\n", j, r->element[j]->str);
        }
    }
}

int main (int argc, char **argv) {
    signal(SIGPIPE, SIG_IGN);
    struct event_base *base = event_base_new();

    redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
    if (c->err) {
        printf("error: %s\n", c->errstr);
        return 1;
    }

    redisLibeventAttach(c, base);
    redisAsyncCommand(c, onMessage, NULL, "SUBSCRIBE testtopic");
    event_base_dispatch(base);
    return 0;
}

所有17条评论

我认为这是一个很好的建议。 我在编写实用代码时对 pub/sub 感到很困惑

+1 为此,我在使用异步 lib​​ev pub/sub 时遇到了困难

标记为ng

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis/hiredis.h"
#include "hiredis/async.h"
#include "hiredis/adapters/libevent.h"

void onMessage(redisAsyncContext *c, void *reply, void *privdata) {
    redisReply *r = reply;
    if (reply == NULL) return;

    if (r->type == REDIS_REPLY_ARRAY) {
        for (int j = 0; j < r->elements; j++) {
            printf("%u) %s\n", j, r->element[j]->str);
        }
    }
}

int main (int argc, char **argv) {
    signal(SIGPIPE, SIG_IGN);
    struct event_base *base = event_base_new();

    redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
    if (c->err) {
        printf("error: %s\n", c->errstr);
        return 1;
    }

    redisLibeventAttach(c, base);
    redisAsyncCommand(c, onMessage, NULL, "SUBSCRIBE testtopic");
    event_base_dispatch(base);
    return 0;
}

在 Ubuntu 14.04 上,需要安装libevent-dev库才能成功编译此示例。 此外,在编译期间需要传递-levent标志。

文档中的问题是它没有写在任何可以订阅某些东西的地方,但是除了订阅其他事件之外不能做任何其他事情。

尝试类似

redisAsyncCommand(c, onMessage, NULL, "SUBSCRIBE testtopic");
redisAsyncCommand(c, onAnotherMessage, NULL, "SUBSCRIBE anothertopic");
redisAsyncCommand(c, onReply, NULL, "SET toto 5");
redisAsyncCommand(c, onReply, NULL, "PUBLISH testtopic \"hello\"");
redisAsyncCommand(c, onReply, NULL, "GET toto");

并且只有前 2 个命令有效。 另一个以REDIS_OK响应,但onReply()回调得到NULL回复。

正确做事的唯一方法是使用 2 个 redis 上下文,一个用于订阅(和 MONITOR),另一个用于其余。 也许应该更新文档以反映此限制?

是否有每个 api 的文档?

您如何捕获套接字最终关闭时的事件?

您如何捕获套接字最终关闭时的事件?

或者您如何快速检测超时并重做新连接? 例如,如果中间的防火墙关闭连接并在之后丢弃数据包。

通过在该连接上只订阅它,我理解它不可能发送一些东西来检查超时。 此外,ASYNC 似乎不存在 TCP 保持连接,无论如何,这些都很难在 docker 容器内的 linux 上正确配置(间隔、重试等)

通常使用 TCP 套接字,您可能会在套接字上有一个短的读/写超时,在您发送某些内容后,您可以查看它是否超时并快速尝试重新连接到另一台服务器或执行其他操作...

如果还有其他东西可以异步发送 ping 命令并配置读/写超时以触发断开连接,那就太好了。

@Gerporgl
在我自己的实现中,我使用了 redisAsyncSetDiconnectCallback 我还没有研究它是如何工作的机制。
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004

在我的情况下,断开连接事件似乎没有足够快地触发(或在某些情况下根本没有触发)对于这种我一直称为“无声断开连接”的情况,一般来说它需要很长时间(我等了超过 5分钟,从未收到该事件,但大约 10-15 分钟后,我终于得到了一个)

在阅读了有关该主题的更多信息后,我注意到您仍然可以在订阅时执行 PINGs 命令,并且发现这篇文章提到您可以订阅自己的 PING 响应事件,就像它是一条已发布的消息:
https://github.com/redis/hiredis/issues/351

从本质上讲,现在似乎运行良好的是例如以 1 秒的间隔发送 PING,如果您不再收到 ping 响应(或者您允许某个容忍阈值),那么您假设连接已死,请进行清理,然后重新连接。 这似乎更加强大和快速。

使用 libevent 和hiredis,您可以在异步处理正常消息传递的同时,使用 PING 的计时器事件执行完整的异步订阅实现。

@Gerporgl
这非常有帮助!
您是否尝试使用相同的 redisAsyncCommand/Async 上下文机制进行键空间事件通知?
我正在努力使那部分工作; 但不幸的是没有收到消息。

你试过这个吗?

redis-cli config set notify-keyspace-events KEA
redisAsyncCommand(c, subscribeCallback, NULL, "PSUBSCRIBE __key*__:*");

@joe-at-startupmedia
谢谢! 我已经尝试过这个..似乎我得到的只是第一个 psubscribe 响应。
数据中的任何更新实际上都不会在回调中收到。
这是示例代码:

`

定义 SUBSCRIBE_CHANNEL "订阅 URLC_Updates"

定义 SUBSCRIBE_KEYEVENT "PSUBSCRIBE '__key __: '"

定义 SCAN_DB "SCAN %d COUNT 100"

定义 QUERY_KEY "GET %s"

空白
onPubsubMessage(redisAsyncContext *c, void *reply, void *privdata)
{
redisReply *r = (redisReply *) 回复;
如果(回复 == NULL)返回;

/* What if reply type is something else... */
if (r->type == REDIS_REPLY_ARRAY) {

    for (int j = 0; j < r->elements; j++) {
        if (r->element[j]->type == REDIS_REPLY_STRING) { 
            myPubsubFile.open(PUBSUB_FILE, fstream::in | fstream::out | fstream::app);
            myPubsubFile << r->element[j]->str << endl;
            myPubsubFile.close();
        } else if (r->element[j]->type == REDIS_REPLY_INTEGER) {
            cout << "Integer : "<< r->element[j]->integer << endl;
        }
    }
}
return;

}
空白
onKeyspaceMessage(redisAsyncContext *c, void *reply, void *privdata)
{
redisReply *r = (redisReply *) 回复;
如果(回复 == NULL)返回;

cout << "Got keyspace event notification from REDIS.. " << endl;

/* What if reply type is something else... */
if (r->type == REDIS_REPLY_ARRAY) {
    cout << "Type is an array.. " << endl;
    cout << "Number of elements here: " << r->elements << endl;

    for (int j = 0; j < r->elements; j++) {
        cout << "\t\t Type for element : " << r->element[j]->type << endl;
        if (r->element[j]->type == REDIS_REPLY_STRING) {
            cout << "\t\t\t" << r->element[j]->str << endl;
        } else if (r->element[j]->type == REDIS_REPLY_INTEGER) {
            cout << "\t\t\t" << "Integer : "<< r->element[j]->integer << endl;
        }   
    }   
} else {
    cout << "This is the type for response : " << r->type << endl;
}   
return;

}
空白*
pubsubRecipient(void* arg)
{
struct event_base *base = event_base_new();

redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
    printf("error: %s\n", c->errstr);
    return NULL;
}   

redisLibeventAttach(c, base);
redisAsyncCommand(c, onPubsubMessage    , NULL, SUBSCRIBE_CHANNEL);
redisAsyncCommand(c, onKeyspaceMessage  , NULL, SUBSCRIBE_KEYEVENT);

`

PS 在 REDIS 中启用 key-space 通知的配置部分在一个单独的进程中; 当我在单独的终端中运行 redis-cli 实例时,我可以看到该配置的效果。

对不起,如果我在这里遗漏了一些东西,但您的 keyevent 订阅字符串是:

 "PSUBSCRIBE 'key*:*'"

它不应该更改为以下内容以匹配该模式吗?

"PSUBSCRIBE __key*__:*"

哦……这个编辑器发生了一些奇怪的事情……当我复制和粘贴我的代码时,周围的下划线出现了。

尽管如此,看起来问题是 -> ' ' <- (单​​引号)围绕我的 _ _ 键 * _ _ 模式。
redis-cli 没有任何问题地接受它们; 但是当我们通过 redis 命令发送它时,这里出了点问题。

解决老问题。 很久以前在 Wiki 中添加了一个示例(感谢 @aluiken)

此页面是否有帮助?
0 / 5 - 0 等级