Eleição de Líder usando Spring Boot
Publicado em
Editado 17/10/2024: se você procura um mecanismo real de lock para jobs, a melhor opção é Shedlock
Alerta: post técnico a frente. Tire as crianças da sala!
O framework Spring Boot tomou de assalto o mercado nos últimos anos. Spring, desde antes, com o MVC já havia absorvido a maior parte de vagas de emprego e questões online, mas com a simplicidade do Boot, praticamente definiu o novo padrão para frameworks em linguagem Java. Com seus recursos de autoconfiguração e o spring iniciatizr, a criação de novos projetos leva minutos ao invés de dias (o que não era incomum no mundo JEE).
Mas toda simplicidade tem um preço: é difícil fazer algo simples e personalizado que fuja do padrão. Talvez difícil não seja a palavra certa, mas quando você se acostuma com a configuração de propriedades simples, todas as complicações se tornam muito frustrantes. Foi assim que comecei minha pequena saga.
Eu estava testando alguns recursos para um novo projeto em andamento e uma das muitas ideias era, dado um número de nós da aplicação, sem intervenção, definir um nó como um líder para executar um trabalho em segundo plano. Simples assim. Tal comportamento é conhecido como Eleição de Líder em Cluster e é um problema comum em muitos ambientes distribuídos (Redis, Mongo entre outros).
A filosofia do Spring é manter seus servidores sem estado (stateless), dessa forma não há necessidade de processo de eleição, uma vez que os vários nós de sua aplicação não são exatamente agrupados, pois eles não tem consciência um do outro. É ótimo para escalar horizontalmente, e eu acredito que é um dos “estilos Spring” que o trouxe para o topo. Mas, no meu caso, tudo que eu queria era uma maneira simples de manter os nós cientes se eles são ou não o líder.
Pesquisando a documentação, eu primeiro encontrei Eleição de líder usando Hazelcast Spring Cloud Cluster , mas ops, está obsoleto em favor do Spring-Integration. Comecei a ler a documentação desse segundo, mas era um exagero imenso para o meu caso de uso. Eu não precisava de interações complexas entre meus nós, nem precisava de grandes integrações entre muitos protocolos diferentes. Fora de cogitação.
A outra maneira que encontrei na comunidade foi usar o ZooKeeper. É um projeto incrível, e que tem por padrão a Eleição de líder que eu tanto precisava. Mas, novamente, configurar um cluster ZooKeeper parecia um grande passo para executar algo simples. Eu não precisava da descoberta de serviços, por exemplo, e dos muitos outros recursos que ele traz para o campo. Parecia um canhão para matar uma formiga. Descartado.
Além desses dois eu encontrei … nada. Foi muito difícil encontrar alguém com o mesmo problema e, quando eu descobri, eles geralmente construíam algo grande e complexo, com tabelas de banco de dados e outros exageros. Não é a ideia de rápido e simples que eu tinha em mente.
Então, eu mesmo construí um.
Usando o redis para criar um registro de bloqueio (lock), a ideia é: Quem detém o bloqueio é o líder. Isso é um pouco diferente de outras implementações onde o nó mais antigo é o líder até o desligamento, porque a liderança pode ir de nó em nó dependendo do ping de bloqueio e do tempo limite de bloqueio, mas atendeu minha necessidade. É simples (dado que eu já tinha o redis rodando como cache distribuido da aplicação), de fácil configuração, confiável e expansível.
Sinta-se à vontade para usá-lo.
É bom testá-lo em, digamos, quatro terminais diferentes. Dessa forma, você pode matar e gerar o processo e verificar a auto-organização dos nós para definir o líder. Uma melhoria seria randomizar os atrasos fixos e verificar o comportamento. Farei esse teste em um futuro próximo.
Por favor, se você encontrar algum bug ou problema, me avise.
Comentários
Sinto que os comentários em blogs têm diminuído com o passar do tempo. Se você tiver alguma dúvida ou quiser falar sobre o post, entre em contato comigo pelos links abaixo.