Olá Habr!
A tradução de hoje aborda não apenas e não muitos microsserviços - um tópico que todo mundo tem nos lábios hoje em dia - mas também lembra o quão importante é chamar uma pá de pá. Às vezes, a transição para a arquitetura de microsserviço é necessária, mas, como o autor mais uma vez enfatiza, exige uma análise cuidadosa das consequências. Aprecie e proveitosa leitura!

De tempos em tempos, faço a mesma pergunta.
Existe uma verdade tão importante que apenas alguns concordam com você? - Peter Thiel
Antes de me sentar neste post, tentei por um longo tempo sobre esse assunto um tópico, que hoje está em uma tendência séria - trata-se de microsserviços. Acho que agora tenho algo a dizer; algumas conclusões são baseadas na reflexão, outras na experiência prática. Então eu te digo.
Vamos começar com uma verdade importante que servirá como ponto de referência para nós ao longo do caminho, como uma estrela polar.
A maioria das implementações de microsserviços nada mais são do que monólitos distribuídos.
Era dos monólitos
Qualquer sistema começou como uma aplicação monolítica. Não vou entrar em detalhes sobre este tópico aqui - muitos já escreveram sobre muitos. No entanto, a maior parte das informações sobre monólitos é dedicada a questões como produtividade e escalabilidade do desenvolvedor, deixando para trás entre parênteses o ativo mais valioso de qualquer empresa de Internet: os dados.
Arquitetura típica de aplicativos monolíticosSe os dados são tão importantes, por que toda a atenção é dada a outros tópicos, mas não a eles? A resposta, em geral, é simples: porque eles não são tão dolorosos quanto a questão dos dados.
Talvez o monólito seja o único estágio do ciclo de vida do sistema em que você:
- Entenda completamente o seu modelo de dados;
- Você pode cooperar com os dados (presume-se que seu banco de dados esteja selecionado corretamente para o seu aplicativo).
Em termos de dados, o monólito é ideal. E como os dados são o ativo mais valioso de qualquer empresa, é melhor não quebrar o monólito, a menos que você tenha um motivo muito bom ou uma combinação desses motivos. Na maioria dos casos, a razão decisiva desse tipo é a necessidade de escalar (já que vivemos no mundo real com suas limitações físicas inerentes).
Quando esse momento chegar, seu sistema provavelmente entrará em uma nova hipóstase: ele se
transformará em um monólito distribuído .
A era dos monólitos distribuídos
Digamos que as coisas estão indo bem na sua empresa e o aplicativo precisa crescer. Você tem mais e mais clientes grandes e seus requisitos para cobrança e geração de relatórios mudaram tanto em termos de um conjunto de oportunidades quanto em seu volume.
Assumindo seriamente a demolição do monólito, em particular, você tentará implementar dois pequenos serviços, um dos quais fornecerá relatórios e o segundo, o faturamento. Muito provavelmente, esses novos serviços fornecerão uma API HTTP e terão um banco de dados dedicado para armazenamento de estado a longo prazo. Depois de muitos commits, você, como nós da
Unbabel , pode obter algo parecido com a ilustração a seguir.
Uma visão geral da arquitetura do sistema após desanexar os serviços de cobrança e relatório do aplicativo monolítico principalTudo está indo conforme o planejado.
- A equipe continua dividindo o monólito em sistemas menores;
- Os transportadores de integração / entrega contínuos funcionam como um relógio;
- O cluster Kubernetes é saudável, os engenheiros são produtivos e felizes com tudo.
A vida é linda.
Mas e se eu disser que agora conspirações vis estão tecendo contra você?
Agora, olhando para o seu sistema, você descobrirá que os dados foram distribuídos por muitos sistemas diferentes. Você começou no estágio em que tinha um banco de dados exclusivo onde todos os objetos de dados estavam armazenados e agora seus objetos de dados se espalharam para lugares diferentes. Talvez você pense que não há problema, pois são necessários microsserviços para criar abstrações e selar dados, ocultando a complexidade interna do sistema.
Você está absolutamente certo. Porém, com o aumento da escala, surgem problemas mais complexos: agora, a qualquer momento, você é obrigado a atender aos requisitos de negócios (por exemplo, para rastrear alguma métrica), exigindo acesso aos dados localizados em mais de um sistema.
O que fazer? De fato, existem muitas opções. Mas com pressa, você também precisa atender a uma enorme fraternidade de clientes que se registraram recentemente com você, para encontrar um equilíbrio entre "rápido" e "bom". Depois de discutir os detalhes, você decide construir um sistema adicional que executaria um certo trabalho de ETL, contribuindo para a solução das tarefas finais. Este sistema precisará ter acesso a todas as réplicas de leitura que contêm as informações necessárias. A figura a seguir mostra como esse sistema poderia funcionar.
Um exemplo generalizado de um sistema analítico de ETL (na Unbabel o chamamos de Análise Automática de Tradução)Na Unbabel, usamos essa abordagem, pois:
- Não afeta muito o desempenho de cada microsserviço;
- Não requer grandes alterações na infraestrutura (basta adicionar um novo microsserviço);
- Fomos capazes de satisfazer com rapidez suficiente nossos requisitos de negócios.
A experiência sugere que, por algum tempo, essa abordagem funcione - até atingir uma certa escala. Na Unbabel, ele nos serviu muito bem até muito recentemente, quando começamos a enfrentar desafios cada vez mais sérios. Aqui estão algumas coisas que se transformaram em dor de cabeça para nós:
1. Alterações de dadosUma das principais vantagens dos microsserviços é o encapsulamento. A representação interna dos dados pode mudar, mas isso não afeta os clientes do sistema, pois eles se comunicam por meio de uma API externa. No entanto, nossa estratégia exigia acesso direto à representação interna dos dados e, portanto, assim que a equipe fez apenas algumas alterações na apresentação dos dados (por exemplo, renomear um campo ou alterar o tipo de
text
para
uuid
), também tivemos que alterar e reimplementar nosso ETL- serviço.
2. A necessidade de processar muitos esquemas de dados diferentesÀ medida que aumentava o número de sistemas aos quais precisávamos conectar, tivemos que lidar com maneiras cada vez mais heterogêneas de apresentar dados. Era óbvio que não podíamos dimensionar todos esses esquemas, as relações entre eles e suas representações.
A raiz de todo malPara obter uma imagem completa do que está acontecendo no sistema, tivemos que parar em uma abordagem semelhante a um monólito. A diferença toda era que não tínhamos um sistema e um banco de dados, mas dezenas desses pares, cada um com sua própria representação de dados; além disso, em alguns casos, os mesmos dados foram replicados em vários sistemas.
Prefiro chamar esse sistema de monólito distribuído. Porque Como é totalmente inadequado para rastrear alterações no sistema, e a única maneira de exibir o estado do sistema é coletar um serviço que se conecta diretamente aos data warehouses de todos os microsserviços. É interessante ver quantos colossos da Internet também enfrentaram desafios semelhantes em algum momento de seu desenvolvimento. Um bom exemplo nesse caso que eu sempre gosto de dar é a rede do LinkedIn.
Esse é o tipo de problema de dados que os fluxos de informações do Linkedin representaram por volta de 2011 - fonteNo momento, você deve estar se perguntando: "o que vocês vão fazer com tudo isso?" A resposta é simples: você precisa começar a acompanhar as alterações e acompanhar as ações importantes à medida que elas ocorrem.
Quebrar um monólito distribuído usando Event SourcingComo praticamente o resto do mundo, os sistemas na Internet são responsivos. Portanto, uma solicitação para a API pode levar à inserção de um novo registro no banco de dados. Atualmente, na maioria dos casos, esses detalhes não nos incomodam, pois estamos interessados principalmente em atualizar o estado do banco de dados. A atualização do estado de um banco de dados é uma consequência condicional de algum evento (nesse caso, uma solicitação de API). O fenômeno do evento é simples e, no entanto, o potencial dos eventos é muito grande - eles podem até ser usados para destruir um monólito distribuído.
Um evento nada mais é do que um fato imutável de alguma modificação que ocorreu no seu sistema . Na arquitetura de microsserviço, os eventos se tornam críticos e ajudam a compreender os fluxos de dados e, com base nisso, deduzir o estado agregado de vários sistemas. Cada microsserviço que executa uma ação interessante do ponto de vista de todo o sistema deve gerar um evento junto com todas as informações essenciais relacionadas ao fato de que esse evento representa.
Talvez você tenha uma pergunta:
"Como os microsserviços que geram eventos me ajudam a resolver o problema do monólito distribuído?"
Se você possui sistemas que geram eventos, pode haver um log de fatos com as seguintes propriedades:
- Falta de ligação a qualquer data warehouse: os eventos geralmente são serializados usando formatos binários como JSON, Avro ou Protobufs;
- Imutabilidade: assim que um evento é gerado, é impossível alterá-lo;
- Reprodutibilidade: o estado do sistema em um determinado momento pode ser restaurado; Para fazer isso, basta "reproduzir" o log de eventos.
Usando esse log, você pode exibir o estado usando qualquer tipo de lógica no nível do aplicativo. Você não está mais associado a nenhum conjunto de microsserviços e
N maneiras pelas quais os dados são apresentados. A única fonte de verdade e seu único armazém de dados agora é o repositório onde seus eventos estão armazenados.
Aqui estão algumas razões pelas quais o log de eventos me parece ser o caminho para ajudar a quebrar o Monólito Distribuído:
1. A única fonte da verdadeEm vez de manter N fontes de dados que podem ser necessárias para conectar-se a (múltiplos) bancos de dados heterogêneos, neste novo cenário, a verdade última é armazenada em exatamente um repositório: o log de eventos.
2. Formato Universal de DadosNa versão anterior do sistema, tivemos que lidar com muitos formatos de dados, pois estávamos diretamente conectados ao banco de dados. No novo layout, podemos agir com muito mais flexibilidade.
Digamos que você gostou de uma foto do Instagram postada por um de seus amigos. Essa ação pode ser descrita: “O
usuário X gostou da imagem P ”. E aqui está um evento que representa esse fato:
Um evento correspondente à abordagem AVO (Ator, Verbo, Objeto), simulando o fato de o usuário selecionar a imagem de que gosta.3. enfraquecimento da comunicação entre produtores e consumidoresPor fim, mas não menos importante, uma das maiores vantagens das operações de eventos é o enfraquecimento efetivo da comunicação entre produtores e consumidores de dados. Essa situação não apenas simplifica o dimensionamento, mas também reduz o número de dependências entre eles. O único contrato restante entre os sistemas nesse caso é o diagrama de eventos.
No início deste artigo, a questão foi colocada: existe uma verdade tão importante na qual apenas alguns concordam com você?
Deixe-me voltar a ele na conclusão desta excursão. Acredito que a maioria das empresas não considera os dados "entidades de primeira classe" quando inicia a migração para a arquitetura de microsserviço. Argumenta-se que todas as alterações de dados ainda podem ser feitas por meio da API, mas essa abordagem leva a uma complicação constante do próprio serviço.
Acredito que a única abordagem verdadeira para capturar alterações de dados em uma arquitetura de microsserviço é fazer com que os sistemas emitam eventos de acordo com um contrato estritamente definido. Ter um log de eventos compilado corretamente permite exibir muitos dados com base em qualquer conjunto de requisitos de negócios. Nesse caso, você apenas precisa aplicar regras diferentes aos mesmos fatos. Em alguns casos, essa fragmentação de dados pode ser evitada se sua empresa (especialmente seus gerentes de produto) tratar os dados como um produto. No entanto, este é um tópico para outro artigo.