Hiredis: Добавить пример кода для pub / sub

Созданный на 15 июл. 2011  ·  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 для этого у меня проблемы с async libev 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, один для подписок (и МОНИТОР), другой для остальных. Может быть, нужно обновить документ, чтобы отразить это ограничение?

Есть ли документ для КАЖДОГО api?

Как вы фиксируете событие, когда сокет в конечном итоге закрывается?

Как вы фиксируете событие, когда сокет в конечном итоге закрывается?

Или как быстро обнаружить тайм-аут и повторить новое соединение? Например, если промежуточный брандмауэр закрывает соединение и после этого отбрасывает пакеты.

Имея только подписку на это соединение, я понимаю, что невозможно отправить что-то для проверки таймаутов. Кроме того, сообщения поддержки активности TCP, похоже, не существуют с ASYNC, и в любом случае их трудно правильно настроить в 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

По сути, то, что сейчас работает хорошо, - это отправлять PING, например, с интервалом в 1 секунду, и если вы больше не получаете ответа 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 "SUBSCRIBE 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, находится в отдельном процессе; и я могу увидеть эффект этой конфигурации, когда я запускаю экземпляр redis-cli в отдельном терминале.

Простите, если мне что-то здесь не хватает, но ваша строка подписки на ключевое событие:

 "PSUBSCRIBE 'key*:*'"

Не следует ли изменить его на следующий, чтобы он соответствовал этому шаблону?

"PSUBSCRIBE __key*__:*"

Ок ... что-то странное происходит с этим редактором ... подчеркивание вокруг клавиши присутствовало, когда я копировал и вставлял свой код.

тем не менее, похоже, что проблема заключалась в -> '' <- (одинарные кавычки) вокруг моего шаблона _ _ key * _ _.
redis-cli принимает их без проблем; но когда мы отправляем его через команду redis, здесь что-то не так.

Перебираем старые вопросы. Пример был добавлен много лет назад в Wiki (спасибо @aluiken)

Была ли эта страница полезной?
0 / 5 - 0 рейтинги