Olá Habr!
A prática mostra que, com a
relevância contínua
do paradigma
dos microsserviços, não faltam
interpretações , críticas e até
desmistificação . Portanto, voltando às nossas publicações traduzidas, decidimos conversar especificamente sobre
microsserviços , ou melhor, considerar uma resposta detalhada à pergunta colocada no título do artigo.
O termo "microsserviços" apareceu pela primeira vez em 2011/2012. Desde então, sem microsserviços no ambiente de TI, quase em lugar nenhum. No entanto, após tantos anos, conseguimos entender adequadamente o que são microsserviços?
Primeiro, vejamos algumas definições:
Os microsserviços são uma abordagem para o desenvolvimento de software (...), segundo a qual um aplicativo é estruturado como um conjunto de serviços vagamente acoplados . Em uma arquitetura de microsserviço, os serviços são altamente detalhados e os protocolos usados são leves . O benefício de decompor um aplicativo em serviços menores individuais é melhorar a modularidade . Nesse formulário, o aplicativo é mais fácil de compreender, desenvolver, testar e o próprio aplicativo se torna mais resistente à erosão da arquitetura. Com essa abordagem da arquitetura, o desenvolvimento é paralelo, e pequenas equipes autônomas têm a oportunidade de desenvolver , implantar e dimensionar independentemente os serviços pelos quais são responsáveis. Também com essa abordagem, a refatoração contínua pode ser aplicada à arquitetura de cada serviço. As arquiteturas de microsserviço também são projetadas para entrega e implantação contínuas .
en.wikipedia.org/wiki/Microservices
Aqui está outra passagem de Martin Fowler:
O termo “arquitetura de microsserviço” foi firmemente enraizado nos últimos anos para descrever uma maneira específica de projetar esses aplicativos, cada um dos quais é um conjunto de serviços implantados independentemente . Embora não exista uma definição exata desse estilo arquitetônico, existem várias características gerais relacionadas à organização do aplicativo em torno de seus recursos de negócios, implantação automatizada, coleta de informações sobre terminais e gerenciamento descentralizado de idiomas e dados .
martinfowler.com/articles/microservices.html
Você provavelmente percebeu que a principal semelhança entre essas duas formulações é a independência de serviços e opções. Agora, vamos tentar responder a algumas perguntas e descobrir se o sistema que você está realmente implementando possui uma base de microsserviço!
O seu sistema permite reiniciar os serviços independentemente?Ou você precisa reiniciar os serviços em uma determinada ordem? Esse comportamento pode ser determinado por sistemas de cluster específicos nos quais o nó se conecta ao nó de semente, por exemplo, no cluster Akka, se não for tratado adequadamente. Outros casos podem estar associados a conexões de longa duração ou soquetes que só podem ser abertos em um lado. Lembre-se de que a comunicação deve permanecer simples e leve. Ainda melhor se for completamente assíncrono e baseado em mensagens. Todas as restrições que fazem com que os serviços dependam um do outro a longo prazo podem se transformar em dificuldades de suporte. Problemas com um dos serviços podem causar problemas em todos os outros serviços que dependem dele, o que pode um dia resultar em uma falha global séria.
Você pode implantar serviços de forma independente?Há casos em que a liberação e a implantação de todos os serviços no sistema devem ocorrer simultaneamente. Esse processo leva vários dias. Obviamente, nesse caso, as possibilidades de introdução da implantação contínua são limitadas, ocorrem atrasos e a capacidade de resposta piora. Leva muito tempo para lidar com bugs urgentes, simplesmente devido aos custos associados ao processo de implantação. Quando equipes diferentes estão ocupadas oferecendo suporte a serviços diferentes, qualquer dependência de implantação pode impedir que outras equipes implantem seus serviços, o que fará com que o trabalho seja suspenso e sua eficácia diminuirá.
Uma modificação pode acarretar a necessidade de fazer modificações sincronizadas em outros serviços?Pode-se imaginar as muitas razões que levaram a esse comportamento. Primeiro de tudo, são mudanças na API. Acontece que basta adicionar um novo campo à solicitação da API para que a compatibilidade entre serviços seja interrompida. No entanto, existem soluções alternativas para ajudar a lidar com essas situações:
- Talvez você queira manter a compatibilidade com versões anteriores obrigatórias para todas as alterações feitas na API; isto é, sempre que uma alteração fundamental é feita, uma nova versão da API é adicionada. Isso não significa que você precisará sempre oferecer suporte a muitas versões da API; você precisará deles apenas pelo período até a migração ser concluída. Você pode simplesmente conversar com outras equipes e garantir que todos tenham completado a migração ou ativar métricas para avaliar se uma API específica ainda está sendo usada.
- Você também pode considerar esta opção: implante várias versões do seu aplicativo ao mesmo tempo, permitindo que outros serviços alternem para a versão mais recente e desabilite a versão anterior.
- Tente também usar formatos que suportem a evolução dos circuitos, por exemplo, Avro , onde ao adicionar uma nova compatibilidade de campo não pode ser violada. Quando um valor para um campo está ausente, o padrão pode ser usado.
Outro motivo para a implantação sincronizada é o uso de código compartilhado por diferentes serviços. Dependendo de como o processo de liberação é criado na parte compartilhada do código, qualquer alteração pode exigir atualização em todos os serviços.
Uma alteração no banco de dados causa uma alteração em muitos serviços?Sim, a comunicação através de um banco de dados é um antipadrão bem conhecido. Em tal situação, é necessário atualizar o esquema e alterar simultaneamente o código de vários microsserviços ao mesmo tempo. É melhor garantir que apenas um serviço seja responsável pelo gerenciamento do circuito. Nesse caso, fica muito mais fácil manter o banco de dados (ou seja, executar suas atualizações) e implementar versões. Se você já estiver em uma situação com uso de banco de dados compartilhado, tente limitar o número de operações de gravação para que outro serviço funcione apenas para consumo.
Você pode atualizar uma dependência ou versão do Java em um único serviço?Em teoria, sempre deve ser possível, certo? Mas realmente não. Existem muitos exemplos de bibliotecas "compartilhadas" ou apenas projetos que contêm definições de todas as dependências usadas no sistema. O objetivo do paradigma de microsserviços é garantir que cada um deles seja uma entidade independente; além disso, diferentes microsserviços nem precisam ser escritos na mesma pilha tecnológica. Ao iniciar um novo projeto, você poderá decidir por si mesmo qual a melhor tecnologia para ela. Qualquer ligação causará novos problemas. Em algum momento, pode ser necessário atualizar para o Scala ou JDK, mas é claro que você não deseja fazer isso imediatamente em todo o sistema. Em um sistema em que o nível de detalhe é observado corretamente, é possível atualizar um serviço individual, implantá-lo, testá-lo e monitorá-lo por vários dias ou semanas e, se tudo estiver correto, você poderá executar atualizações no restante do sistema. No entanto, quando existem mecanismos
common
ou outros fortes de unificação, essas possibilidades são severamente limitadas.
A estrutura ou biblioteca escolhida o força a permanecer na mesma escolha em outros serviços?Algumas bibliotecas são bastante invasivas. Quando você decide integrá-los a sistemas externos, pode acontecer de repente que sua escolha não é tão boa quando você implementa um novo serviço em uma pilha tecnológica diferente. Os custos associados à sua integração no mesmo espírito que o restante dos serviços podem ser simplesmente insuportáveis.
Suponha, por exemplo, que você tenha vários serviços escritos no
NodeJS que usam ativamente
JSON: API . Em seguida, você deseja implementar um novo serviço no Scala e parece que não há uma biblioteca cliente adequada (há alguns anos tivemos um caso semelhante, mas agora a situação nessa área melhorou um pouco).
A falha de um único serviço causa uma falha em todo o sistema?Suponha que a comunicação em seu sistema seja completamente síncrona. Um serviço faz uma solicitação para outro e aguarda uma resposta dele. Se o primeiro serviço falhar, o segundo serviço não poderá funcionar. Se esta for a situação no seu sistema, tente implementar nele uma comunicação assíncrona com base em mensagens. Pode ser um pouco mais difícil simular todos os processos, mas essa abordagem possibilita suavizar os efeitos das falhas.
Nos casos em que isso não é possível, você pode tentar limitar temporariamente o conjunto de recursos do aplicativo. Por exemplo, na página da loja on-line, são exibidas informações sobre o produto e revisões, além disso, as informações e as revisões do produto vêm de duas fontes diferentes. Se o microsserviço de revisões não funcionar, você ainda poderá exibir a página do produto simplesmente informando ao usuário que o sistema de revisão está temporariamente indisponível.
Existe algum componente compartilhado no seu sistema?Você tem código compartilhado? Configuração? Alguma coisa? Sim, essa é uma pergunta não trivial. Os defensores da limpeza dos microsserviços acreditam que é melhor não permitir entidades compartilhadas no sistema! É melhor copiar do que código dividido. Na prática, às vezes é considerado aceitável ter várias
bibliotecas compartilhadas , mas com limitações estritas. Talvez sejam bibliotecas contendo arquivos
proto
para
buffers de protocolo ou apenas objetos POJO comuns. Melhor evitar dependências nessas bibliotecas. Tivemos um caso em que uma biblioteca cliente simples que causava conflitos de dependência em um sistema grande. Em algum momento, começou a parecer que os elementos mais antigos do aplicativo estavam usando a antiga biblioteca HTTP, que não pôde ser atualizada. Felizmente, essas situações podem ser evitadas usando a renomeação de dependências no processo de construção (sombreamento de dependência), mas tenha muito cuidado.
O dano causado pela coesão excessiva dos serviços é muito mais prejudicial por problemas causados pela repetibilidade do código.
Sam Newman, Criando microsserviços
ConclusõesA implementação do sistema perfeito baseado em microsserviços não é tarefa fácil. As pessoas tendem a "cortar custos", introduzindo laços estreitos no sistema decorrentes do código compartilhado e, em algum momento, esse sistema na prática se transforma em um monólito distribuído. Freqüentemente, isso acontece se os microsserviços começarem a ser usados já no início do projeto, quando a área de assunto ainda não estiver bem estudada e você não tiver um sólido histórico pronto no DevOps. É melhor começar com um
monólito adequadamente estruturado , onde todas as áreas de responsabilidade são totalmente conhecidas e são facilmente separadas umas das outras. No entanto, desde o início, você precisa ter certeza de que seu sistema foi projetado de maneira
limpa e que a organização do pacote é mantida em ordem, o que subsequentemente fornece fácil migração de código para novos serviços (por exemplo, um único pacote "quase de nível superior" pode ser a base para um novo microsserviço) .
O ArchUnit pode ajudar na análise de dependências que surgem entre pacotes.
(...) você não deve começar imediatamente a trabalhar em um novo projeto com a introdução de microsserviços, mesmo se tiver certeza de que seu aplicativo será grande o suficiente para que os microsserviços se justifiquem.
martinfowler.com/bliki/MonolithFirst.html
Lembre-se de que os microsserviços devem simplificar o desenvolvimento e o suporte! Tudo o que você precisa é de serviços pouco acoplados, cada um dos quais pode ser implantado independentemente dos outros.