Hiredis: Codebeispiel für pub/sub hinzufügen

Erstellt am 15. Juli 2011  ·  17Kommentare  ·  Quelle: redis/hiredis

ng

Hilfreichster Kommentar

#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;
}

Alle 17 Kommentare

Ich denke, es ist ein guter Vorschlag. Ich bin ziemlich verwirrt über Pub/Sub beim Schreiben von praktischem Code

+1 dafür, ich habe Schwierigkeiten mit async libev pub/sub

Gekennzeichnet für 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;
}

Unter Ubuntu 14.04 müssen libevent-dev Bibliotheken installiert werden, um dieses Beispiel erfolgreich zu kompilieren. Außerdem muss das Flag -levent während der Kompilierung übergeben werden.

Das Problem in der Dokumentation ist, dass nirgendwo geschrieben steht, dass man etwas ABONNIEREN kann, dann aber nichts anderes tun kann, als andere Events zu abonnieren.

Probiere etwas aus wie

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");

Und nur die ersten 2 Befehle funktionieren. Der andere antwortet mit REDIS_OK aber der onReply() Rückruf erhält eine NULL Antwort.

Die einzige Möglichkeit, die Dinge richtig zu machen, besteht darin, 2 Redis-Kontexte zu verwenden, einen für die Abonnenten (und MONITOR), den anderen für den Rest. Vielleicht sollte das Dokument aktualisiert werden, um diese Einschränkung widerzuspiegeln?

Gibt es ein Dokument für JEDE API?

Wie erfassen Sie ein Ereignis, wenn der Socket schließlich geschlossen wird?

Wie erfassen Sie ein Ereignis, wenn der Socket schließlich geschlossen wird?

Oder wie erkennt man schnell ein Timeout und stellt eine neue Verbindung wieder her? Zum Beispiel, wenn eine Firewall dazwischen die Verbindung schließt und danach Pakete verwirft.

Dadurch, dass nur das Abonnieren dieser Verbindung vorhanden ist, ist es nach meinem Verständnis unmöglich, etwas zu senden, um nach Zeitüberschreitungen zu suchen. Auch TCP-Keepalives scheinen bei ASYNC nicht zu existieren und diese sind unter Linux beispielsweise in einem Docker-Container schwer richtig zu konfigurieren (die Intervalle, Wiederholungen usw.)

Normalerweise kann es bei TCP-Sockets zu einem kurzen Lese-/Schreib-Timeout auf dem Socket kommen und nachdem Sie etwas gesendet haben, können Sie sehen, ob es Timeouts gibt, und schnell versuchen, sich wieder mit einem anderen Server zu verbinden oder etwas anderes zu tun ...

Wenn es etwas anderes gäbe, das asynchron einen Ping-Befehl senden und die Lese-/Schreib-Timeouts konfigurieren könnte, um Verbindungsabbrüche auszulösen, wäre das schön.

@Gerporgl
In meiner eigenen Implementierung habe ich redisAsyncSetDiconnectCallback verwendet. Ich habe den Mechanismus nicht erforscht, wie dies funktioniert.
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004

In meinem Fall scheint das Disconnect-Ereignis nicht schnell genug (oder in einigen Fällen überhaupt nicht) ausgelöst zu werden, für diesen Fall von dem, was ich immer eine "stille Trennung" nannte, im Allgemeinen dauert es viel zu lange (ich habe mehr als 5 . gewartet Minuten und habe die Veranstaltung nie erhalten, aber nach 10-15 Minuten habe ich endlich eine bekommen)

Nachdem ich ein bisschen mehr über das Thema gelesen hatte, bemerkte ich, dass Sie PINGs-Befehle weiterhin ausführen können, während Sie abonniert sind.
https://github.com/redis/hiredis/issues/351

Was jetzt im Wesentlichen gut zu funktionieren scheint, ist das Senden von PINGs zum Beispiel im 1-Sekunden-Intervall, und wenn Sie keine Ping-Antwort mehr erhalten (oder eine bestimmte Toleranzschwelle zulassen), gehen Sie davon aus, dass die Verbindung tot ist, führen Sie Ihre Bereinigung durch, dann wieder verbinden. Das wirkt viel robuster und schneller.

Mit libevent und Hiredis können Sie eine vollständige asynchrone Subskriptionsimplementierung mit Zeitgeberereignissen für PING durchführen, während die normale Nachrichtenübermittlung asynchron abgewickelt wird.

@Gerporgl
Dies war sehr hilfreich!
Haben Sie zufällig versucht, denselben redisAsyncCommand/Async-Kontextmechanismus für Schlüsselraumereignisbenachrichtigungen zu verwenden?
Ich versuche, diesen Teil zum Laufen zu bringen; bekomme aber leider keine meldungen.

Hast du das probiert?

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

@joe-at-startupmedia
Vielen Dank! Ich habe dies bereits versucht.. und es scheint, dass alles, was ich zurückbekomme, die erste psubscribe-Antwort ist.
Etwaige Aktualisierungen der Daten werden im Rückruf nicht tatsächlich empfangen.
Hier ist Beispielcode:

`

define SUBSCRIBE_CHANNEL "SUBSCRIBE URLC_Updates"

define SUBSCRIBE_KEYEVENT "PSUBSCRIBE '__key __: '"

Definiere SCAN_DB "SCAN %d COUNT 100"

definiere QUERY_KEY "GET %s"

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

/* 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;

}
Leere
onKeyspaceMessage(redisAsyncContext *c, void *reply, void *privdata)
{
redisReply *r = (redisReply *) Antwort;
if (Antwort == NULL) return;

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;

}
Leere*
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 Der Konfigurationsteil, der Schlüsselraumbenachrichtigungen in REDIS aktiviert, befindet sich in einem separaten Prozess; und ich kann die Auswirkungen dieser Konfiguration sehen, wenn ich eine Instanz von redis-cli in einem separaten Terminal ausführe.

Pardon Wenn ich hier etwas übersehe, aber Ihr Keyevent-Abonnement-String lautet:

 "PSUBSCRIBE 'key*:*'"

Sollte es nicht wie folgt geändert werden, um diesem Muster zu entsprechen?

"PSUBSCRIBE __key*__:*"

Ohk.. etwas Seltsames passiert mit diesem Editor... die Unterstriche um den Schlüssel waren vorhanden, als ich meinen Code kopierte und einfügte.

Trotzdem sieht es so aus, als ob das Problem -> ' ' <- (einfache Anführungszeichen) um meinen _ _ Schlüssel * _ _ Muster herum war.
redis-cli akzeptiert sie ohne Probleme; aber wenn wir es per redis-Befehl senden, stimmt hier etwas nicht.

Alte Themen durchgehen. Ein Beispiel wurde vor Ewigkeiten zum Wiki hinzugefügt (danke @aluiken)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen