Staged Event Driven Architecture (SEDA)
Aqui no Magalu, nossas aplicações enfrentam cargas muito altas de processamento. Por hora, processamos pedidos, consultas de estoque, emitimos notas fiscais e uma infinidade de operações que chegam facilmente na casa dos milhões.
Por isso, construir aplicações com alta performance e escalabilidade está longe de ser um luxo por aqui, é uma necessidade. Recentemente pude participar de um projeto muito bacana e gostaria de compartilhar a experiência com vocês.
O desafio
Atualmente o Magalu possui contratos com diversas transportadoras, além de malha própria para a realização de entregas em todo o território nacional. Ter informações de prazo e preço desatualizadas impacta diretamente nossos clientes, causando uma péssima experiência.
O desafio era automatizar o sincronismo dessas informações diariamente, acabando com o processo manual. Processo aliás, feito por diversos analistas do CD e com incontáveis planilhas de Excel que chegava a levar semanas.
Por conta do volume de operações que essa aplicação teria que aguentar, escalabilidade seria um fator decisivo. E foi por isso que construímos nossa aplicação utilizando arquitetura SEDA.
SEDA?
SEDA é o acrônimo para Staged Event Driven Architecture. Simplificando ao extremo e no meu entendimento, seu princípio é realizar o processamento do todo em pequenos estágios conectados por filas. Exemplos de aplicações atuais que utilizam esse modelo de processamento são o Mule ESB e o Apache Cassandra.
Quando uma requisição chega ao app, ela entra em uma espécie de esteira, onde passa por cada estágio, onde um pool de processadores atua na mensagem e a envia para o próximo estágio, até que todos os estágios sejam percorridos.
E quais os benefícios dessa arquitetura?
- Desacoplamento total entre os estágios nos permitiria balancear a carga deles de maneira independente: Ou seja, estágios complexos, com um tempo de processamento mais elevado podem receber um número de consumidores maior do que estágios simples.
- Alta escalabilidade, uma vez que para aumentar o throughput precisamos apenas de mais processos consumindo mensagens das filas ou de mais processadores nos pools.
Módulos da aplicação
A aplicação foi escrita em Go e conta com dois módulos: um scheduler e o worker.
Scheduler
O scheduler apenas executa como uma rotina, recuperando os registros que precisam ser processados e os enfileirando na primeira fila do cluster ActiveMQ. A partir dali, o trabalho é realizado pelos processos do worker.
Worker
Módulo responsável pelo processamento das mensagens. Aqui, de acordo com os pesos configurados, criamos as goroutines que recuperam mensagens das filas do ActiveMQ e também os pools de processadores, que processam essas mensagens e as enviam para a fila seguinte.
Resultados
O projeto levou pouco mais de 2 semanas para ficar pronto. Até a data de escrita deste artigo, diariamente mantemos atualizadas mais de 306 mil rotas, realizando mais de 4 milhões de requisições em APIs de transportadoras parceiras em um pouco menos de 2 horas.
Aprendizados da abordagem
Como em todo projeto, sempre tiramos algumas lições:
- Para não precisarmos gerir a infraestrutura, certificados, SSL etc, optamos por utilizar o serviço do AmazonMQ. Não se preocupar com infra nos deu tempo de sobra para atacar outros pontos da aplicação como mecanismos de re-tentativa, tuning do número de processos, construção de métricas e alarmes etc
- Ter um broker nesses cenários pode ser perigoso por ser um ponto único de falha. Felizmente, o AmazonMQ oferece configuração de failover, mas a inda assim a sua aplicação precisa saber como se comportar caso o broker principal caia.
- Geração de métricas é importante em qualquer app, mas em processos assíncronos o peso delas aumenta exponencialmente. A rastreabilidade se torna um fator de suma importância. O que usamos no projeto foi um ID gerado de acordo com algumas propriedades da rota, o que nos permitiu rastrear o registro por toda a trilha de execução.
Espero que vocês tenham gostado, até a próxima!