Hiredis: Agregar ejemplo de código para pub / sub

Creado en 15 jul. 2011  ·  17Comentarios  ·  Fuente: redis/hiredis

ng

Comentario más útil

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

Todos 17 comentarios

Creo que es una buena sugerencia. Estoy bastante confundido acerca de pub / sub al escribir código práctico.

+1 para esto, tengo dificultades con async libev pub / sub

Marcado por 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;
}

En Ubuntu 14.04, las bibliotecas libevent-dev deben instalarse para compilar correctamente este ejemplo. Además, la bandera -levent debe pasarse durante la compilación.

El problema en la documentación es que no está escrito en ningún lugar que pueda SUSCRIBIRSE para algo, pero luego no puede hacer nada más que suscribirse a otros eventos.

Prueba algo como

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

Y solo los 2 primeros comandos funcionarán. El otro responde con REDIS_OK pero la devolución onReply() llamada NULL .

La única forma de hacer las cosas correctamente es usar 2 contextos redis, uno para los suscriptores (y MONITOR), el otro para el resto. ¿Quizás el documento debería actualizarse para reflejar esta limitación?

¿Hay un documento para CADA api?

¿Cómo se captura un evento para cuando finalmente se cierra el socket?

¿Cómo se captura un evento para cuando finalmente se cierra el socket?

¿O cómo detecta rápidamente un tiempo de espera y rehace una nueva conexión? Por ejemplo, si un cortafuegos en el medio cierra la conexión y descarta paquetes después.

Al tener solo la suscripción en esa conexión, es imposible, según tengo entendido, enviar algo para verificar los tiempos de espera. Además, los keepalives de TCP no parecen existir con ASYNC y, de todos modos, son difíciles de configurar correctamente en Linux dentro de un contenedor docker, por ejemplo (los intervalos, reintentos, etc.)

Normalmente, con los sockets TCP, podría tener un breve tiempo de espera de lectura / escritura en el socket y, después de enviar algo, puede ver si se agota el tiempo de espera e intentar reconectarse rápidamente a otro servidor o hacer otra cosa ...

Si hubiera algo más que pudiera enviar un comando ping de forma asincrónica y configurar los tiempos de espera de lectura / escritura para desencadenar desconexiones, sería bueno.

@Gerporgl
En mi propia implementación utilicé redisAsyncSetDiconnectCallback No he investigado el mecanismo de cómo funciona esto.
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004

En mi caso, el evento de desconexión no parece dispararse lo suficientemente rápido (o no en absoluto en algunos casos) para este caso de lo que siempre llamé una "desconexión silenciosa", en general lleva demasiado tiempo (esperé más de 5 minutos y nunca recibí el evento, pero luego de 10 a 15 minutos finalmente obtuve uno)

Después de leer un poco más sobre el tema, noté que aún puedes ejecutar el comando PING mientras estás suscrito, y encontré que esta publicación menciona que puedes suscribirte a tu propio evento de respuesta PING como si fuera un mensaje publicado:
https://github.com/redis/hiredis/issues/351

Básicamente, lo que parece funcionar bien ahora es enviar PING, por ejemplo, en un intervalo de 1 segundo, y si no recibe más respuesta de ping (o permite un cierto umbral de tolerancia), entonces asume que la conexión está muerta, haga su limpieza, luego reconectarse. Eso parece mucho más robusto y rápido.

Con libevent y hiredis, puede realizar una implementación de suscripción asíncrona completa utilizando eventos de temporizador para PING mientras maneja la entrega normal de mensajes de forma asincrónica.

@Gerporgl
¡Esto fue muy útil!
Por casualidad, ¿intentó utilizar el mismo mecanismo de contexto redisAsyncCommand / Async para las notificaciones de eventos de espacio clave?
Estoy tratando de hacer que esa parte funcione; pero lamentablemente no recibo los mensajes.

¿Has probado esto?

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

@ joe-en-startupmedia
¡Gracias! Ya probé esto ... y parece que todo lo que obtengo es la primera respuesta de suscripción.
Las actualizaciones de los datos no se reciben realmente en la devolución de llamada.
Aquí hay un código de muestra:

'

definir SUBSCRIBE_CHANNEL "SUBSCRIBE URLC_Updates"

define SUBSCRIBE_KEYEVENT "PSUBSCRIBE '__key __: '"

definir SCAN_DB "SCAN% d COUNT 100"

definir QUERY_KEY "GET% s"

vacío
onPubsubMessage (redisAsyncContext * c, void * respuesta, void * privdata)
{
redisReply * r = (redisReply *) respuesta;
if (respuesta == 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;

}
vacío
onKeyspaceMessage (redisAsyncContext * c, void * respuesta, void * privdata)
{
redisReply * r = (redisReply *) respuesta;
if (respuesta == 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;

}
vacío*
pubsubRecipient (vacío * 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);

'

PD: La parte de configuración, que habilita las notificaciones de espacio de claves en REDIS, se encuentra en un proceso separado; y puedo ver el efecto de esa configuración, cuando ejecuto una instancia de redis-cli en una terminal separada.

Perdón si me falta algo aquí, pero su cadena de suscripción de evento clave es:

 "PSUBSCRIBE 'key*:*'"

¿No debería cambiarse a lo siguiente para que coincida con ese patrón?

"PSUBSCRIBE __key*__:*"

Ohk ... algo extraño está pasando con este editor ... los guiones bajos que rodean la clave estaban presentes cuando copié y pegué mi código.

sin embargo, parece que el problema era -> '' <- (comillas simples) alrededor de mi patrón _ _ key * _ _.
redis-cli los acepta sin problemas; pero cuando lo enviamos a través del comando redis, hay algo mal aquí.

Pasando por viejos problemas. Hace años que se agregó un ejemplo a la Wiki (gracias @aluiken)

¿Fue útil esta página
0 / 5 - 0 calificaciones