Como implementar uma fila de mensagens no Redis?

29

Por que Redis para filas?

Tenho a impressão de que o Redis pode ser um bom candidato para a implementação de um sistema de filas. Até este momento, usamos nosso banco de dados MySQL com polling, ou RabbitMQ. Com o RabbitMQ, tivemos muitos problemas - as bibliotecas clientes são muito ruins e com erros e gostaríamos de não investir muitas horas de desenvolvedor para corrigi-las, alguns problemas com o console de gerenciamento de servidores etc. E, por enquanto sendo, pelo menos, não estamos buscando milissegundos ou pressionando seriamente o desempenho, desde que um sistema tenha uma arquitetura que ofereça suporte a uma fila de maneira inteligente, provavelmente estamos em boa forma.

Ok, então esse é o plano de fundo. Essencialmente, eu tenho um modelo de fila simples e muito clássico - vários produtores produzindo trabalho e vários consumidores consumindo trabalho, e tanto produtores quanto consumidores precisam ser capazes de escalar de maneira inteligente. Acontece que um ingênuo PUBSUBnão funciona, já que não quero que todos os assinantes consumam trabalho, só quero que um assinante receba o trabalho. Na primeira passagem, parece-me BRPOPLPUSHum design inteligente.

Podemos usar o BRPOPLPUSH?

O design básico BRPOPLPUSHé que você tem uma fila de trabalho e uma fila de progresso. Quando um consumidor recebe trabalho, ele empurra atomicamente o item para a fila de progresso e, quando conclui o trabalho, LREMé ele. Isso evita a interrupção do trabalho se os clientes morrem e torna o monitoramento bastante fácil - por exemplo, podemos dizer se há um problema que leva os consumidores a demorar muito tempo para executar tarefas, além de saber se há um grande volume de tarefas.

Garante

  • o trabalho é entregue a exatamente um consumidor
  • o trabalho acaba em uma fila de progresso, por isso não pode ser um buraco negro se um consumidor

As desvantagens

  • Parece-me bastante estranho que o melhor design que eu encontrei não seja realmente usado, PUBSUBpois esse parece ser o foco da maioria das postagens de blogs sobre filas no Redis. Então, sinto que estou perdendo algo óbvio. A única maneira que eu vejo usando PUBSUBsem consumir tarefas duas vezes é simplesmente enviar uma notificação de que o trabalho chegou, o que os consumidores podem então não bloquear RPOPLPUSH.
  • É impossível solicitar mais de um item de trabalho por vez, o que parece ser um problema de desempenho. Não é enorme para a nossa situação, mas obviamente diz que esta operação não foi projetada para alto rendimento ou para esta situação
  • Em resumo: estou perdendo alguma coisa estúpida?

Também adicionando a tag node.js, porque esse é o idioma com o qual estou lidando principalmente. O nó pode oferecer algumas simplificações na implementação, dada a natureza de thread único e não bloqueador, mas além disso, estou usando a biblioteca e as soluções node-redis e devem ou podem ser sensíveis a seus pontos fortes e fracos.

djechlin
fonte

Respostas:

5

Se você deseja usar o Redis para uma fila de mensagens no Node.js e não se importa de usar um módulo, tente o RSMQ - a Fila de mensagens simples do Redis para o nó. Não estava disponível no momento em que essa pergunta foi feita, mas hoje é uma opção viável.

Se você realmente deseja implementar a fila como indicado na sua pergunta, pode ler a fonte do RSMQ, porque são apenas 20 telas de código que fazem exatamente o que você está pedindo.

Vejo:

rsp
fonte
Aceito isso, a menos que eu saiba mais tarde que é realmente defeituoso ou quebrado ou algo assim.
djechlin
22

Até agora, encontrei algumas dificuldades que gostaria de documentar aqui.

Como você lida com a reconexão lógica?

Esse é um problema difícil e especialmente difícil ao projetar e implementar uma fila de mensagens. As mensagens devem poder enfileirar-se em algum lugar quando os consumidores estiverem offline, para que um simples pub-sub não seja forte o suficiente e os consumidores precisem se reconectar em um estado de escuta. Os pops de bloqueio são difíceis de manter, porque são um estado de escuta não idempotente . A escuta deve ser uma operação idempotente, mas, ao lidar com uma desconexão em relação a um pop bloqueador, você tem o prazer de pensar muito se a desconexão ocorreu logo após a operação ter sido bem-sucedida ou pouco antes da falha da operação. Isso não é intransponível, mas é indesejável.

Além disso, a operação de escuta deve ser o mais simples possível. Idealmente, ele deve ter essas propriedades:

  • Ouvir é idempotente.
  • O consumidor está sempre ouvindo e a lógica de limitação é processada fora do código da lógica de escuta. O RabbitMQ encapsula isso, permitindo que o consumidor limite o número de mensagens não embaladas que ele pode ter.
    Particularmente, fui com um design deficiente, no qual a reintrodução de um pop bloqueador dependia do sucesso de operações anteriores, que eram frágeis e exigiam muita reflexão.

Agora estou favorecendo uma solução Redis PUBSUB + RPOPLPUSH. Isso dissocia a notificação do trabalho do consumo de trabalho, o que permite determinar uma solução de escuta limpa. O PUBSUB é o único responsável pela notificação do trabalho. A natureza atômica do RPOPLPUSH é responsável pelo consumo e pela delegação do trabalho a exatamente um consumidor. A princípio, essa solução parecia desnecessariamente complicada em comparação com um pop bloqueador, mas agora vejo que a complicação não era desnecessária; estava resolvendo um problema difícil.

No entanto, esta solução não é trivial:

  • os consumidores também devem verificar se há trabalho para se reconectar.
  • os consumidores podem querer fazer uma pesquisa de novos trabalhos de qualquer maneira, por redundância. Se a pesquisa realmente for bem-sucedida, um aviso deve ser emitido, pois isso deve ocorrer apenas entre o consumo no PUBSUB e a pesquisa em um RPOPLPUSH. Portanto, muitos sucessos nas pesquisas indicam um sistema de assinatura quebrado.

Observe que o design PUBSUB / RPOPLPUSH também possui problemas de dimensionamento. Todo consumidor recebe uma notificação leve de cada mensagem, o que significa que há um gargalo desnecessário. Suspeito que seja possível usar canais para fragmentar o trabalho, mas esse provavelmente é um projeto complicado para funcionar bem.

djechlin
fonte
Não tenho certeza se sigo o problema de bloquear consumidores. Parece-me que, se não houver trabalho para processar, os consumidores deverão bloquear até que haja algum, embora eu suponha que se o consumidor também estiver fazendo outras coisas que possam ser uma história diferente, mas isso não é mais um problema no aplicativo e não tanto para a fila? O IE não consideraria um bloqueio de encadeamento em um aplicativo maior uma solução mais elegante, onde o encadeamento poderia notificar o aplicativo quando recuperasse um trabalho da fila. Talvez seja apenas o uso do nó que esteja criando a complicação.
AaronM
9
Estou curioso para saber até onde você chegou desde agosto do ano passado. Você conseguiu resolver seus problemas de maneira satisfatória? Como você os resolveu?
AaronM
3
AAA: assim como @AaronM, eu adoraria ouvir como você progrediu.
precisa saber é
Acordado. Como isso progrediu? Eu gosto da idéia de remover o RabbitMQ da pilha e usar o Redis, que está lá de qualquer maneira. Meu problema é como registrar um consumidor usando o RSMQ (nó lib).
ra9r
@raiglstorfer não ter trabalhado lá por dois anos: P sinta-se livre para pesquisar e post ...
djechlin
0

Portanto, o maior motivo para optar por usar o RabbitMQ sobre o Redis são os cenários de falha e o armazenamento em cluster.

Este artigo realmente explica melhor, então fornecerei o link:

https://aphyr.com/posts/283-jepsen-redis

O redis sentinel e, mais recentemente, o clustering redis não conseguem lidar com vários cenários de falha muito básicos que o tornaram uma má escolha para uma fila.

O RabbitMQ tem seu próprio conjunto de problemas, no entanto, sendo dito que é incrivelmente sólido na produção e é uma boa fila de mensagens.

Aqui está o post para coelho:

https://aphyr.com/posts/315-jepsen-rabbitmq

Quando você olha para o teorema do CAP (consistência, disponibilidade e manipulação de partição), você pode escolher apenas 2 de 3. Estamos alavancando o RMQ para o CP (manipulação de consistência e partição) com nosso carregamento de mensagens, se estiver indisponível, não é ' o fim do mundo. Para não perder mensagens, usamos ignorar para o tratamento da partição para não perder mensagens. As duplicatas podem ser manipuladas, pois a fonte gerencia o UUID.

ra9r
fonte