Hiredis: Ajouter un exemple de code pour pub/sub

Créé le 15 juil. 2011  ·  17Commentaires  ·  Source: redis/hiredis

ng

Commentaire le plus utile

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

Tous les 17 commentaires

Je pense que c'est une bonne suggestion. Je suis assez confus au sujet de pub/sub dans l'écriture de code pratique

+1 pour cela, j'ai des difficultés avec async libev pub/sub

Marqué pour 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;
}

Sur Ubuntu 14.04, les bibliothèques libevent-dev doivent être installées pour compiler avec succès cet exemple. De plus, l'indicateur -levent doit être passé lors de la compilation.

Le problème dans la documentation est qu'il n'est écrit nulle part que vous pouvez vous ABONNER à quelque chose, mais que vous ne pouvez alors rien faire d'autre que de vous abonner à d'autres événements.

Essayez quelque chose comme

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

Et seules les 2 premières commandes fonctionneront. L'autre répond avec REDIS_OK mais le rappel onReply() obtient une réponse NULL .

La seule façon de faire les choses correctement est d'utiliser 2 contextes redis, un pour les abonnés (et MONITOR), l'autre pour le reste. Peut-être que la doc devrait être mise à jour pour refléter cette limitation ?

Existe-t-il un document pour CHAQUE API ?

Comment capturez-vous un événement lorsque le socket se ferme finalement ?

Comment capturez-vous un événement lorsque le socket se ferme finalement ?

Ou comment détecter rapidement un timeout et refaire une nouvelle connexion ? Par exemple, si un pare-feu entre les deux ferme la connexion et supprime les paquets après.

En n'ayant que l'abonnement sur cette connexion, il est impossible, d'après ce que je comprends, d'envoyer quelque chose pour vérifier les délais d'attente. De plus, les keepalives TCP ne semblent pas exister avec ASYNC et de toute façon, elles sont difficiles à configurer correctement sous Linux dans un conteneur docker par exemple (les intervalles, les réessais, etc.)

Normalement, avec les sockets TCP, vous pouvez avoir un court délai de lecture/écriture sur le socket et après avoir envoyé quelque chose, vous pouvez voir s'il expire et essayer rapidement de vous reconnecter à un autre serveur ou de faire autre chose...

S'il y avait autre chose qui pouvait envoyer une commande ping de manière asynchrone et configurer les délais de lecture/écriture pour déclencher des déconnexions, ce serait bien.

@Gerporgl
Dans ma propre implémentation, j'ai utilisé redisAsyncSetDiconnectCallback. Je n'ai pas recherché le mécanisme de son fonctionnement.
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004

Dans mon cas, l'événement de déconnexion ne semble pas être déclenché assez rapidement (ou pas du tout dans certains cas) pour ce cas de ce que j'ai toujours appelé une "déconnexion silencieuse", en général cela prend beaucoup trop de temps (j'ai attendu plus de 5 minutes et je n'ai jamais reçu l'événement, mais après environ 10 à 15 minutes, j'en ai finalement eu un)

Après avoir lu un peu plus sur le sujet, j'ai remarqué que vous pouvez toujours exécuter la commande PING lorsque vous êtes abonné, et j'ai trouvé ce message mentionnant que vous pouvez vous abonner à votre propre événement de réponse PING comme s'il s'agissait d'un message publié :
https://github.com/redis/hiredis/issues/351

Essentiellement, ce qui semble bien fonctionner maintenant, c'est d'envoyer des PING par exemple à 1 seconde d'intervalle, et si vous ne recevez plus de réponse de ping (ou si vous autorisez un certain seuil de tolérance), vous supposez que la connexion est morte, faites votre nettoyage, puis reconnecter. Cela semble beaucoup plus robuste et rapide.

En utilisant libevent et hiringis, vous pouvez effectuer une implémentation d'abonnement asynchrone complète à l'aide d'événements de minuterie pour PING tout en gérant la livraison normale des messages de manière asynchrone.

@Gerporgl
C'était très utile !
Par hasard, avez-vous essayé d'utiliser le même mécanisme de contexte redisAsyncCommand/Async pour les notifications d'événements d'espace clé ?
J'essaie de faire fonctionner cette partie ; mais malheureusement pas reçu les messages.

Avez-vous essayé cela?

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

@joe-at-startupmedia
Merci! J'ai déjà essayé ceci .. et il semble que tout ce que je reçois est la première réponse de psubscribe.
Aucune mise à jour des données n'est réellement reçue dans le rappel.
Voici un exemple de code :

`

définir SUBSCRIBE_CHANNEL "SUBSCRIBE URLC_Updates"

définir SUBSCRIBE_KEYEVENT "PSUBSCRIBE '__key __: '"

définir SCAN_DB "SCAN %d COUNT 100"

définir QUERY_KEY "GET %s"

annuler
onPubsubMessage(redisAsyncContext *c, void *reply, void *privdata)
{
redisReply *r = (redisReply *) réponse ;
if (réponse == 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;

}
annuler
onKeyspaceMessage(redisAsyncContext *c, void *reply, void *privdata)
{
redisReply *r = (redisReply *) réponse ;
if (répondre == 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;

}
annuler*
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 La partie de configuration, qui active les notifications d'espace de clé dans REDIS, est dans un processus distinct ; et je peux voir l'effet de cette configuration, lorsque j'exécute une instance de redis-cli dans un terminal séparé.

Excusez-moi s'il me manque quelque chose ici mais que votre chaîne d'abonnement keyevent est :

 "PSUBSCRIBE 'key*:*'"

Ne devrait-il pas être remplacé par ce qui suit pour correspondre à ce modèle ?

"PSUBSCRIBE __key*__:*"

Ohk... quelque chose de bizarre se passe avec cet éditeur... les traits de soulignement entourant la clé étaient présents lorsque j'ai copié et collé mon code.

néanmoins, il semble que le problème était -> ' ' <- (guillemets simples) entourant mon motif _ _ key * _ _.
redis-cli les accepte sans aucun problème ; mais lorsque nous l'envoyons via la commande redis, il y a quelque chose qui ne va pas ici.

Passer par d'anciens problèmes. Un exemple a été ajouté il y a longtemps au Wiki (merci @aluiken)

Cette page vous a été utile?
0 / 5 - 0 notes