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 PUBSUB
nã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 BRPOPLPUSH
um 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,
PUBSUB
pois 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 usandoPUBSUB
sem consumir tarefas duas vezes é simplesmente enviar uma notificação de que o trabalho chegou, o que os consumidores podem então não bloquearRPOPLPUSH
. - É 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.
fonte
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:
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:
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.
fonte
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.
fonte