Hiredis: Adicionar exemplo de código para pub / sub

Criado em 15 jul. 2011  ·  17Comentários  ·  Fonte: redis/hiredis

ng

Comentários muito úteis

#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 comentários

Acho que é uma boa sugestão. Estou muito confuso sobre pub / sub ao escrever código prático

1 para isso, estou tendo dificuldades com pub / sub libev assíncrono

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

No Ubuntu 14.04, libevent-dev bibliotecas precisam ser instaladas para compilar com sucesso este exemplo. Além disso, a bandeira -levent precisa ser passada durante a compilação.

O problema na documentação é que não está escrito em nenhum lugar que você possa ASSINAR para algo, mas não pode fazer nada além de assinar outros eventos.

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

E apenas os 2 primeiros comandos funcionarão. O outro responde com REDIS_OK mas o retorno de chamada onReply() recebe uma resposta NULL .

A única maneira de fazer as coisas corretamente é usar 2 contextos do redis, um para os assinantes (e MONITOR), o outro para os demais. Talvez o documento deva ser atualizado para refletir essa limitação?

Existe um documento para CADA API?

Como você captura um evento para quando o soquete finalmente fecha?

Como você captura um evento para quando o soquete finalmente fecha?

Ou como você detecta rapidamente um tempo limite e refaz uma nova conexão? Por exemplo, se um firewall entre fechar a conexão e descartar os pacotes depois.

Por ter apenas a assinatura nessa conexão, torna impossível, pelo que entendi, enviar algo para verificar os tempos limite. Além disso, as manutenções de atividade TCP não parecem existir com ASYNC e, de qualquer forma, são difíceis de configurar corretamente no Linux dentro de um contêiner docker, por exemplo (os intervalos, novas tentativas, etc.)

Normalmente, com soquetes TCP, você pode ter um curto tempo limite de leitura / gravação no soquete e, depois de enviar algo, você pode ver se ele atinge o tempo limite e tentar rapidamente se reconectar a outro servidor ou fazer outra coisa ...

Se houvesse algo mais que pudesse enviar um comando ping de forma assíncrona e configurar os tempos limite de leitura / gravação para acionar desconexões, isso seria ótimo.

@Gerporgl
Em minha própria implementação, usei redisAsyncSetDiconnectCallback. Não pesquisei o mecanismo de como isso funciona.
https://github.com/nidhhoggr/twemproxy_sentinel/commit/602e07cfdbd57a307ff008e8e9d41909ac34b004

No meu caso, o evento de desconexão não parece ser disparado rápido o suficiente (ou nem um pouco em alguns casos) para este caso do que sempre chamei de "desconexão silenciosa", em geral leva muito tempo (esperei mais de 5 minutos e nunca recebi o evento, mas depois de 10-15 minutos finalmente consegui um)

Depois de ler um pouco mais sobre o assunto, percebi que você ainda pode fazer o comando PINGs enquanto estiver inscrito, e descobri que esse post menciona que você pode se inscrever em seu próprio evento de resposta PING como se fosse uma mensagem publicada:
https://github.com/redis/hiredis/issues/351

Essencialmente, o que parece funcionar bem agora é enviar PINGs, por exemplo, em um intervalo de 1 segundo, e se você não receber mais resposta de ping (ou permitir um certo limite de tolerância), então você presume que a conexão está inativa, faça sua limpeza, então reconecte. Isso parece muito mais robusto e rápido.

Usando libevent e hiredis, você pode fazer uma implementação de assinatura assíncrona completa usando eventos de cronômetro para PING enquanto lida com a entrega normal de mensagens de forma assíncrona.

@Gerporgl
Isso foi muito útil!
Por acaso você tentou usar o mesmo mecanismo de contexto redisAsyncCommand / Async para notificações de eventos de espaço-chave?
Estou tentando fazer essa parte funcionar; mas infelizmente não recebo as mensagens.

Você já tentou isso?

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

@ joe-at-startupmedia
Obrigado! Eu já tentei isso ... e parece que tudo que estou recebendo de volta é a primeira resposta do psubscribe.
Quaisquer atualizações nos dados não são realmente recebidas no retorno de chamada.
Aqui está o código de amostra:

`

definir SUBSCRIBE_CHANNEL "SUBSCRIBE URLC_Updates"

define SUBSCRIBE_KEYEVENT "PSUBSCRIBE '__key __: '"

definir SCAN_DB "SCAN% d COUNT 100"

definir QUERY_KEY "GET% s"

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

}
vazio
onKeyspaceMessage (redisAsyncContext * c, void * reply, void * privdata)
{
redisReply * r = (redisReply *) responder;
if (resposta == 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;

}
vazio*
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 A parte de configuração, que ativa notificações de espaço de chave no REDIS, está em um processo separado; e posso ver o efeito dessa configuração, quando executo uma instância de redis-cli em um terminal separado.

Desculpe se estou faltando alguma coisa aqui, mas sua string de assinatura de evento-chave é:

 "PSUBSCRIBE 'key*:*'"

Não deve ser alterado para o seguinte para corresponder a esse padrão?

"PSUBSCRIBE __key*__:*"

Ohk .. algo estranho está acontecendo com este editor ... os sublinhados ao redor da chave estavam presentes quando eu copiei e colei meu código.

no entanto, parece que o problema era -> '' <- (aspas simples) em torno do meu padrão _ _ key * _ _.
redis-cli os aceita sem problemas; mas quando o enviamos via comando redis, há algo errado aqui.

Repassando problemas antigos. Um exemplo foi adicionado há muito tempo ao Wiki (obrigado @aluiken)

Esta página foi útil?
0 / 5 - 0 avaliações