fbpx
Resiliência: Polly vs RabbitMQ
Publicado em: quinta-feira, 23 de set de 2021
Categorias: RabbitMQ de A a Z

Fato que eu adoro essas comparações inusitadas! A intenção desse post é poder trazer de forma consistente uma visão que ajude a trazer clareza, e me permita também referenciar um conteúdo mais estruturado em vez de ter de construir toda uma argumentação em locais que propositalmente não possuem esse espaço para isso.

Nos últimos meses falando de RabbitMQ eu escutei algumas falácias a respeito do Polly e vamos desmistificar o assunto. Bora falar de resiliência de forma séria.

Se você acha que Polly é a solução dos seus problemas na hora de entregar resiliência, senta aqui, vamos conversar! O buraco é muito mais em baixo.

O primeiro ponto a ressaltar é que Polly é uma boa solução, uma boa library, e inclusive uso em conjunto com RabbitMQ.

Mas se você pensa que usar SOMENTE Polly,

vai te trazer a tal resiliência,

eu vou te provar que isso não é verdade!

Se Polly é sua única iniciativa para trazer resiliência, você não tem um projeto resiliente.

O que é o Polly?

De forma prática é:

[EN-US] Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. From version 6.0.1, Polly targets .NET Standard 1.1 and 2.0 .

[PT-BR] Polly é uma biblioteca .NET de resiliência e tratamento de falhas transitórias que permite aos desenvolvedores expressar políticas como Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback de maneira fluente e thread-safe. A partir da versão 6.0.1, o Polly tem como alvo o .NET Standard 1.1 e 2.0.

Se você acha que Polly é tudo que precisa, com certeza você ignorou “transient-fault-handling”. Ou não viu como implementar, não percebeu onde está o problema.

O ponto principal é que o Polly não é capaz de tolerar falhas que façam a aplicação cair. Ou seja, lida com exceptions, mas não capaz de retornar o processamento após uma queda do processo ou mesmo um novo deployment.

Em um fluxo de retentativa, por exemplo, ele vai tentar de acordo com sua configuração tentar algumas vezes, vai conseguir aguardar algum tempo até executar novamente a tarefa, e se você programar pensando nisso, vai deixar uma task para algumas operações, só para isolar esse processamento da thread principal …

…mas vamos supor que já tenham se passado 10 minutos, sem conseguir concluir uma tarefa, como você trata isso?

Você quando faz deploy analisa tarefas não concluídas? Onde? Como?

Você primeiro tira a carga de trabalho

Espera todas as tarefas em retry serem executadas

pra somente depois desas 2 condições, matar as instâncias antes de fazer seu deployment?

Vamos supor que ao invés de levar 10 minutos, por alguma instabilidade essa atividade esteja durando 5 minutos. Seu usuário está aguardando a atividade por todo esse tempo? Preso na operação?

Vale ressaltar, já pela 2a vez, que isso não é demérito do Polly. Em qualquer cenário em que sua aplicação não caia, e a thread possa ficar presa aguardando a execução, será ótimo usá-lo e ele vai te ajudar a mitigar problemas pontuais, mas não adianta achar que é uma bala de prata, porque não é.

Podemos tratar resiliência de forma muito mais séria do que isso

Quando pensamos em usar Polly partimos do pressuposto que nossa aplicação continuará no ar, a instância continuará em memória, e o processo continuará em execução.

Essa é uma premissa fraca que não se sustenta, pois ninguém dá 100% de uptime de garantia para nada. Sim seu processo pode cair, seu container pode morrer, seu nó pode cair inteiro, seu cluster pode capotar, e em casos extremos até seu datacenter pode ficar inoperante. Já vimos isso acontecer.

Qual a ideia sob essas premissas caóticas? Orabolas, tolerar e torná-la o menos destrutível possível!

E o RabbitMQ tem exatamente essa função.

Seu papel de mediação, e suas configurações de resiliência fazem exatamente isso.

  • A mensagem pode ser durável (armazenada em disco)
  • As filas podem ser duráveis (tolerar restart do servidor)
  • Nas mensagens duráveis com filas duráveis, as mensagens são persistidas em disco antes da entrega para os consumidores.
  • O envio pode ser resiliente (confirmando recebimento da mensagem)
  • O consumo pode ser resiliente (confirmando o processamento da mensagem)
  • As quorum queues são filas distribuídas que usam Raft Consensus Algorithm para distribuir mensagens no cluster e assegurar essas mensagens em todo o cluster.

Isso é o máximo de resiliência que você pode esperar da vida!

Dá para ir além? Dá, mas aí envolveria os custos de implantar internet e um cluster de servidores em outro planeta!

Eu detalhei esses pontos no post RabbitMQ é Resiliente?

Resiliência com RabbitMQ

Por exemplo, em janeiro eu comecei o HUB. O Hub consiste em um broker de eventos, que recebe mensagens de diversos players e envia para o RabbitMQ, lá no Message Broker residem as configurações de aplicam um destino certo para cada mensagem. Cada mensagem tem uma característica diferente e essas mensagens são agrupadas em filas de processamento.

Algumas mensagens já são duplicadas e processadas de bate-pronto, outras não.

O ponto é que desde janeiro, eu tenho somente uma fração dos serviços consumindo essas mensagens, ou seja, a maioria dos serviços estão offline simplesmente porque nunca foram implementados.

Essa é a resiliência com RabbitMQ. São 9 meses de espera, aguardando um consumidor.

Esse projeto é detalhado no post Hub de Eventos que também tem link para um vídeo em que eu detalho isso.

Claro que esse é um modelo exótico, propositalmente feito para produzir esse impacto, mas é uma boa demonstração do poder de resiliência com RabbitMQ.

Polly + RabbitMQ

Agora chegamos a um assunto que me encanta. As possibilidades de combinar ambos. É um tesão falar disso!

Que tal falarmos de pagamento?

RabbitMQ como Fallback

Essa estratégia é arriscada, pois o processo pode cair, o servidor inteiro pode capotar, e aí já era.

Mas é possível tentar executar uma operação de durante a transação do usuário e caso não consiga, enviar uma mensagem para o RabbitMQ como forma de agendar a execução futura.

RabbitMQ + Polly

No consumo de uma fila, você pode adicionar o polly para fazer retentativas de processamento: 1, 2, 3 ou mais tentativas se quiser. Se não for possível concluir então deixamos a exception estourar, e nossa abstração em cima do RabbitMQ vai tratar e vai devolver a mensagem para a fila.

Note, é importante usar os mecanismos de resiliência do RabbitMQ.

Polly no start das aplicações que usam RabbitMQ

Embora em produção isso aconteça em uma proporção mínima, o fato é que o RabbitMQ, em geral, é mais lerdo para subir do que nossas aplicações. E sentimos muito isso durante o desenvolvimento, principalmente em cenários onde usamos o Docker Compose em projetos .NET.

Usar polly para essa estratégia de subida da aplicação é incrível! Vale a pena.

Você pode ver isso no Startup.cs do projeto RabbitMQ-Walkthrough

// Exemplo 1
services.AddTransientWithRetry<IConnection, BrokerUnreachableException>(sp => sp.GetRequiredService<ConnectionFactory>().CreateConnection());


// Exemplo 2
services.AddTransientWithRetry<SqlConnection, SqlException>(sp =>
{
	SqlConnection connection = new SqlConnection("..."); //intencionalmente inline, é feito para não trocar por causa da dependência do grafana (que depende dessa connectionstring)
	connection.Open();
	return connection;
});

Ambos os cenários usam uma política de retry encapsulada em uma Excension Method

public static IServiceCollection AddTransientWithRetry<TService, TKnowException>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory)
	where TKnowException : Exception
	where TService : class
{
	return services.AddTransient(sp =>
	{
		TService returnValue = default;

		RetryPolicy policy = BuildPolicy<TKnowException>();

		policy.Execute(() =>
		{
			returnValue = implementationFactory(sp);

		});

		return returnValue;

	});
}


private static RetryPolicy BuildPolicy<TKnowException>(int retryCount = 5) where TKnowException : Exception
{
	return Policy
		.Handle<TKnowException>()
		.WaitAndRetry(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
	);
}

Conclusão e considerações finais

Polly é uma solução incrível, mas achar que é o bastante para trazer resiliência é ingenuidade.

RabbitMQ também não é bala de prata. Em um deployment clusterizado, se todos os servidores do cluster perderem seus discos simultaneamente, não há o que fazer também, assim como seu banco de dados e tudo mais que você conhece, seja dentro e fora de um cloud provider pequeno ou grande, em uma infra gerenciada ou IaaS.

Resiliência a tudo, não existe, o que fazemos é mitigar riscos e trabalhar com probabilidades.

Se extrapolarmos os níveis de resiliência, vamos começar pensando na thread, depois vamos pensar em processo, servidor, cluster, região, cloud, continente e planeta… se chegarmos ao limite, não temos como manter nosso software resiliente à uma desintegração da terra, por exemplo.

Aliás, nesse caso nem precisaríamos mais do projeto, mas ok, acho que deu para entender meu ponto!

Portanto,

  • a probabilidade do seu processo cair é alta, seja por bugs, ou por demandas de deployment
  • mas a probabilidade do servidor/nó inteiro cair é menor, por se tratar de algo mais estável e com menos mudanças
  • já a probabilidade do servidor de RabbitMQ cair, é menor ainda, já que não realizamos alterações quase nunca
  • enquanto a probabilidade de um cluster regional cair, é bem menor, pois precisaria de um evento catastrófico
  • ao mesmo tempo a probabilidade de um cluster multicloud cair é menor ainda, pois 2 clouds diferentes precisariam parar ao mesmo tempo.

E na verdade quando falamos do RabbitMQ o que nos preocupa não é uma eventual queda, mas a queima dos discos, seja por um evento corriqueiro ou por uma catástrofe. A queda em si, é tolerada sem problemas, da mesma forma que seu banco de dados tolera restarts do servidor de banco.

Portanto, é menos provável que um cluster distribuídos em regiões diferentes do mundo, pare ao mesmo tempo. Enquanto em cenários single server, se o servidor inteiro perder o disco, não há o que fazer.

O ponto é que o nível de resiliência entregue com RabbitMQ está em outra esfera e ordem de de grandeza, mas também exige outro nível de experiência e pela natureza assíncrona e distribuída, não é nada intuitivo.

É intuitivo no mundo real, mas não é no universo tecnológico que vivemos, pelo menos por enquanto não.

Assim a combinação dos 2 é muito bem vinda, mas os níveis de entrega de resiliência são incomparáveis.

Você pediu e agora virou curso. Mensageria .NET é minha formação de especialista em RabbitMQ com .NET, onde ensino RabbitMQ do básico, cada fundamento, cada detalhe, ao avançado.

Onde você vai sair do zero absoluto e vai conseguir criar, projetar e corrigir soluções .NET com RabbitMQ.

Além de contar mais 3 outros bonus incríveis para ajudar todos que precisam de um up na carreira.

RabbitMQ Newsletter

Novidades e ofertas de conteúdo exclusivo e único no Brasil.

Hoje com orgulho somos referência quando se fala em RabbitMQ com .NET.

São quase 10 anos usando RabbitMQ em projetos .NET com C#, implantando, convencendo times e mostrando o caminho para aslcançar sos 5 benefícios.

Após centenas de pedidos, criei um curso dedicado aos profissionais .NET. 

Aqui nessa newsletter eu te entrego promoções e links especiais! Cola aqui, tem muita coisa legal!

Luiz Carlos Faria

Meu primeiro contato com RabbitMQ foi em 2013.

Eu estava sozinho na definição de uma arquitetura para a reestruturação de uma integração enquanto meu time estava ocupado com o dia-a-dia.

Naquela época eu precisava de apenas 1 ou 2 recursos que o RabbitMQ entregava.

Nas primeiras semanas e meses em produção pude perceber coisas que não estavam escritas em lugar algum, benefícios e formas de uso das quais poderiam resolver anos de frustração.

Desde então RabbitMQ tem sido meu aliado na restruturação de projetos dos mais variados.

E por mais simples que seja, ainda é possível, 10 anos depois, gerar surpresas com novas abordagens que geram novos benefícios.

7 dias

É tudo que precisa para sair do zero, à produção!

Com conforto, com segurança, e com apoio.

Desde que você já seja um desenvolvedor profissional.

Se você quer entregar mais Disponibilidade, Eficiência, Resiliência, Confiabilidade e/ou Escalabilidade, em projetos .NET, aqui é o seu lugar.

0 comentários

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.