Como as arquiteturas de sistema de microsserviço evitam gargalos de rede?

72

Eu tenho lido muito sobre arquiteturas de microsserviço para aplicativos de servidor e me pergunto como o uso da rede interna não é um gargalo ou uma desvantagem significativa em comparação a uma arquitetura monolítica.

Por uma questão de precisão, aqui estão minhas interpretações dos dois termos:

  1. Arquitetura monolítica: um aplicativo em um único idioma que lida com todas as funcionalidades, dados etc. Um balanceador de carga distribui solicitações do usuário final em várias máquinas, cada uma executando uma instância de nosso aplicativo.

  2. Arquitetura de microsserviço : Muitos aplicativos (microsserviços) lidam com uma pequena parte da funcionalidade e dos dados. Cada microsserviço expõe uma API comum que é acessada através da rede (em oposição à comunicação entre processos ou memória compartilhada na mesma máquina). As chamadas de API são fixadas principalmente no servidor para produzir uma página, embora talvez parte desse trabalho seja feito pelo cliente consultando microsserviços individuais.

Para minha imaginação ingênua, parece que uma arquitetura de microsserviços usa tráfego de rede lento em vez de recursos mais rápidos na mesma máquina (a memória e o disco). Como garantir que a consulta da API pela rede interna não diminua o tempo de resposta geral?

James Mishra
fonte
A rede interna costuma ter 1 Gbps, às vezes mais rápido. Pense no tamanho médio da resposta JSON de uma API. Quantas dessas respostas podem ser transmitidas por uma conexão de 1 Gbps em um segundo?
Arseni Mourzenko
3
se você acha que precisa de microsserviços - e pode! - dois livros excelentes para preparar: amazon.com/Building-Microservices-Sam-Newman/dp/1491950358 e amazon.com/Release-It-Production-Ready-Pragmatic-Programmers/dp/…
Steven A. Lowe
@ MainMa, o problema não está na largura de banda, mas no atraso. E se você precisa de ida e volta, você ficaria surpreso com o quão pouco real largura de banda que você pode usar
Stephan Eggermont

Respostas:

61

As redes internas costumam usar conexões de 1 Gbps ou mais rápidas. As conexões de fibra óptica ou ligação permitem larguras de banda muito maiores entre os servidores. Agora imagine o tamanho médio de uma resposta JSON de uma API. Quanto dessas respostas pode ser transmitida por uma conexão de 1 Gbps em um segundo?

Vamos realmente fazer as contas. 1 Gbps é 131 072 KB por segundo. Se uma resposta JSON média for de 5 KB (o que é bastante!), Você poderá enviar 26 214 respostas por segundo através do cabo com apenas um par de máquinas . Não é tão ruim, não é?

É por isso que a conexão de rede geralmente não é o gargalo.

Outro aspecto dos microsserviços é que você pode dimensionar facilmente. Imagine dois servidores, um hospedando a API e outro consumindo-a. Se alguma vez a conexão se tornar um gargalo, basta adicionar dois outros servidores e você poderá dobrar o desempenho.

É quando nossas 26 214 respostas anteriores por segundo se tornam muito pequenas para a escala do aplicativo. Você adiciona outros nove pares e agora pode servir 262 140 respostas.

Mas vamos voltar ao nosso par de servidores e fazer algumas comparações.

  • Se uma consulta média não armazenada em cache em um banco de dados demorar 10 ms., Você estará limitado a 100 consultas por segundo. 100 consultas. 26 214 respostas. Atingir a velocidade de 26 214 respostas por segundo requer uma grande quantidade de cache e otimização (se a resposta realmente precisar fazer algo útil, como consultar um banco de dados; as respostas no estilo "Hello World" não se qualificam).

  • No meu computador, no momento, o DOMContentLoaded para a página inicial do Google teve 394 ms. depois que a solicitação foi enviada. Isso é menos de 3 solicitações por segundo. Para a página inicial do Programmers.SE, ocorreram 603 ms. depois que a solicitação foi enviada. Isso não é nem 2 solicitações por segundo. A propósito, eu tenho uma conexão de internet de 100 Mbps e um computador rápido: muitos usuários esperam mais.

    Se o gargalo for a velocidade da rede entre os servidores, esses dois sites poderão literalmente fazer milhares de chamadas para APIs diferentes enquanto veiculam a página.

Esses dois casos mostram que a rede provavelmente não será o seu gargalo na teoria (na prática, você deve fazer os benchmarks e os perfis reais para determinar a localização exata do gargalo do seu sistema específico hospedado em um hardware específico). O tempo gasto realizando o trabalho real (seria consultas SQL, compactação, qualquer que seja) e enviando o resultado ao usuário final é muito mais importante.

Pense em bancos de dados

Geralmente, os bancos de dados são hospedados separadamente do aplicativo Web que os utiliza. Isso pode suscitar uma preocupação: e a velocidade da conexão entre o servidor que hospeda o aplicativo e o servidor que hospeda o banco de dados?

Parece que há casos em que a velocidade da conexão se torna problemática, ou seja, quando você armazena grandes quantidades de dados que não precisam ser processados ​​pelo próprio banco de dados e devem estar disponíveis agora (ou seja, arquivos binários grandes). Mas tais situações são raras: na maioria dos casos, a velocidade de transferência não é tão grande em comparação com a velocidade de processamento da própria consulta.

Quando a velocidade de transferência realmente importa é quando uma empresa está hospedando grandes conjuntos de dados em um NAS, e o NAS é acessado por vários clientes ao mesmo tempo. É aqui que uma SAN pode ser uma solução. Dito isto, esta não é a única solução. Os cabos Cat 6 podem suportar velocidades de até 10 Gbps; A ligação também pode ser usada para aumentar a velocidade sem alterar os cabos ou os adaptadores de rede. Existem outras soluções, envolvendo replicação de dados em vários NAS.

Esqueça a velocidade; pense em escalabilidade

Um ponto importante de um aplicativo Web é poder escalar. Embora os desempenhos reais sejam importantes (porque ninguém quer pagar por servidores mais poderosos), a escalabilidade é muito mais importante, porque permite que você jogue hardware adicional quando necessário.

  • Se você tiver um aplicativo não particularmente rápido, perderá dinheiro porque precisará de servidores mais poderosos.

  • Se você tiver um aplicativo rápido que não pode ser dimensionado, perderá clientes porque não poderá responder a uma demanda crescente.

Da mesma forma, as máquinas virtuais foram vistas há uma década como um enorme problema de desempenho. De fato, hospedar um aplicativo em um servidor versus hospedá-lo em uma máquina virtual teve um impacto importante no desempenho. Embora a diferença seja muito menor hoje, ela ainda existe.

Apesar dessa perda de desempenho, os ambientes virtuais se tornaram muito populares devido à flexibilidade que oferecem.

Assim como na velocidade da rede, você pode achar que a VM é o gargalo real e, dada a sua escala real, você economizará bilhões de dólares hospedando seu aplicativo diretamente, sem as VMs. Mas não é isso que acontece em 99,9% dos aplicativos: o gargalo deles está em outro lugar, e a desvantagem de uma perda de alguns microssegundos por causa da VM é facilmente compensada pelos benefícios da abstração e escalabilidade de hardware.

Arseni Mourzenko
fonte
Claro, podemos dizer que as respostas JSON são pequenas, mas e quanto à quantidade ? Sinto que um site com carga pesada teria mais tráfego de rede em uma arquitetura de microsserviço do que uma arquitetura monolítica (onde o único tráfego de rede é de / para o (s) servidor (es) de banco de dados). O armazenamento em cache pode ajudar, mas, em tempo real e / ou conteúdo gerado dinamicamente, não sei até onde iria o armazenamento em cache.
James Mishra
@ JamesMishra: editei minha resposta para responder às suas preocupações.
Arseni Mourzenko 10/03/2015
Sua resposta é perfeita . Você não apenas respondeu a todas as objeções em que consigo pensar, mas também respondeu a objeções em que não pensei.
James Mishra
5
Meus 2 centavos do mundo real: um sistema composto por microsserviços muito faladores pode sofrer problemas de desempenho apenas devido a uma rede bloqueada. O armazenamento em cache e um design baseado em Fluxo de Eventos são seus amigos nesses casos. Além da rede, CPU e memória, um sistema baseado em microsserviço também precisa incorporar resiliência em seu design: o que acontece se um microsserviço estiver inoperante? Como você constrói novas tentativas, transações distribuídas, auto-cura, monitoramento - Sugiro à procura de "você deve ser este alto para usar microservices"
Sudhanshu Mishra
4
Corrija-me se estiver errado, mas até onde eu sei, se você tiver uma rede de 1 Gbps, significa que você pode enviar teoricamente 1 Gb de dados por segundo por essa rede. Independentemente do número de conexões. Quanto maior o número de conexões, menor a largura de banda para cada conexão. Portanto, seu limite real sem atualizar sua rede para suportar uma largura de banda maior seria 26.214 respostas por segundo. Adicionar mais servidores não aumentará a largura de banda da sua rede. Se um único cluster puder gerar essa quantidade de tráfego, adicionar mais servidores gerando ainda mais dados congestionará sua rede.
Sebbe 18/07
7

Eu acho que você está lendo muito na parte 'micro'. Isso não significa substituir todas as classes por um serviço de rede, mas componente um aplicativo monolítico em componentes de tamanho sensato, cada um lidando com um aspecto do seu programa. Os serviços não se comunicam, portanto, na pior das hipóteses, você dividiu uma solicitação de rede grande em várias menores. Os dados retornados não serão significativamente diferentes dos que você recebe (embora você possa retornar mais dados e consolidá-los no cliente)

gbjbaanb
fonte
3
"Os serviços não conversam entre si." Imagino que os microsserviços possam ter dependências compartilhadas (autenticação, talvez?) Que um possa separar em outro microsserviço. O LDAP, em certo sentido, é um microsserviço de autenticação e imagino que todos os outros microsserviços conversem com ele. Ou ... a autenticação acontece apenas uma vez? Como cada microsserviço verifica as permissões na autenticação para impedir ataques ao Direct Object Access?
James Mishra
2
@ JamesMishra bem .. depende. Quando usei a arquitetura de microsserviço pela última vez, cada serviço era totalmente independente dos outros por questões de segurança (mas também por razões corporativas de silos). A autenticação foi tratada por cada um de maneira diferente, embora controlada por uma política de arquitetura. Ainda assim, não há motivo para que eles não pudessem conversar com o auth, por exemplo, ou apenas ter o auth com base em uma biblioteca. Mas ... eu estava tentando dizer que eles não deveriam passar muitas chamadas entre si, não que eles não devessem consumir serviços como clientes.
precisa
@JamesMishra, auth é frequentemente o seu próprio serviço nesses ambientes, portanto, cada serviço deve fazer uso disso em vez de fazer uma implementação completa.
Paul
2

Estruturando o acesso ao código e aos recursos, de forma que o sistema resultante possa ser flexível o suficiente para ser executado como um aplicativo monolítico ou um aplicativo distribuído via configuração. Se você abstrair o mecanismo de comunicação por trás de alguma interface comum e criar seu sistema com a simultaneidade em mente, poderá otimizar tudo facilmente depois de criar um perfil do sistema e encontrar os gargalos reais da garrafa.

mortalapeman
fonte
Um exemplo para explicar o que eu suponho que @mortalapeman significa: você tem uma interface jr / c # IProductAvailibitiy onde todos os consumidores IProductAvailibitiy estão vinculados. Há também uma classe ProductAvailibitiyImpl que implementa essa interface e um ProductAvailibitiyMicroservice que usa ProductAvailibitiyImpl. Os consumidores podem ser configurados para usar um ProductAvailibitiyImpl local ou um proxy remoto para ProductAvailibitiyMicroservice
k3b
2

Gostaria de adicionar uma perspectiva diferente, de um setor diferente, com suposições muito diferentes - simulação distribuída (no nível da entidade). Conceitualmente, isso é muito parecido com um videogame FPS distribuído. Principais diferenças: todos os jogadores compartilham algum estado: onde o dragão está agora; nenhuma chamada ao banco de dados; tudo é mantido na RAM para velocidade e baixa latência, o rendimento é menos relevante (mas acho que você também não pode ignorá-lo).

Você pode pensar em cada aplicativo participante como um monólito (que representa todas as facetas de um jogador) ou como um microsserviço (que representa apenas um único jogador na multidão).

Meus colegas têm demonstrado interesse em dividir um único aplicativo participante, ainda mais em microsserviços menores que podem ser compartilhados, por exemplo, arbitragem de danos ou cálculos de linha de visão, coisas que geralmente são agrupadas nas simulações.

O problema é a latência do envio de chamadas e a espera de solicitações. A largura de banda é irrelevante e abundante de qualquer maneira, como outros já apontaram. Mas se um cálculo de linha de visão passar de 1 microseg a 100 microseg (por exemplo, devido ao enfileiramento no novo microsserviço compartilhado entre todos os aplicativos do player), será uma perda enorme (pode ser necessário vários ou muitos cálculos de linha de visão para cada atualização, várias atualizações / segundo).

Pense com muito cuidado sobre como os serviços funcionam, quando são chamados e quais dados são trocados. Nossos aplicativos já não trocam apenas informações de posição, eles trocam informações de acerto de contas - estou na posição x, seguindo na direção y na velocidade q. E não preciso atualizar minhas informações até que essas suposições mudem. Muito menos atualizações e latência (embora ainda sejam um problema) surgem proporcionalmente com menos frequência.

Portanto, em vez de solicitar um serviço em uma granulação fina com uma frequência mais alta, tente diminuir a frequência:

  1. alterar quais dados são solicitados e usar cálculos locais
  2. enviando parâmetros de consulta ou gatilho para uma resposta assíncrona
  3. pedidos em lote
  4. antecipação de pedidos e preparação prévia de uma resposta, especulação (oposta à avaliação preguiçosa)
  5. sempre que possível, evite microsserviços chamando outros microsserviços; isso agrava o problema, obviamente. Entendo que isso é um incentivo para aumentar os microsserviços e derrotar um pouco a questão, mas eles não são amigos da latência. Talvez apenas admita e supere.

Agora lembre-se de verificar suas suposições sobre o seu sistema. Se você está mais preocupado com a taxa de transferência do que com a latência, ou se não possui estado compartilhado etc., use todos os meios de microsserviços onde eles fizerem sentido. Só estou dizendo que talvez não as use onde não fazem sentido.

Capitão Aporam
fonte
1

Sua imaginação ingênua está certa. E muitas vezes isso não importa. Máquinas modernas são rápidas. As principais vantagens da arquitetura de microsserviço são vistas no esforço e no tempo de desenvolvimento e manutenção.

E, é claro, não há regra dizendo que você não pode usar memória compartilhada ou mesmo implantar fisicamente vários serviços em um executável. Desde que você o projete, não será dependente disso.

Stephan Eggermont
fonte
CPUs são rápidas. A memória está rápida. SSDs são rápidos. Mas as placas de rede, os roteadores e os switches são "rápidos"? Outra resposta insiste, mas não tenho certeza.
James Mishra
Definitivamente, é fácil encontrar problemas de velocidade da rede. Execute um serviço em São Francisco, outro em Amsterdã e consuma-os em Sydney. Atraso é a chave, não a largura de banda. Então não faça isso. E tornar os serviços tão grande quanto faz sentido
Stephan Eggermont
1

Como muitas pessoas mencionaram, não se trata de gargalos na rede. É mais sobre fragilidade da rede. Portanto, o primeiro passo é evitar a comunicação síncrona. É mais fácil do que parece. Tudo que você precisa é de serviços com limites corretos. Os limites certos resultam em serviços autônomos, pouco acoplados e altamente coesos. Um bom serviço não precisa de informações de outro serviço, ele já possui. A única maneira pela qual bons serviços se comunicam é através de eventos. Os bons serviços também são consistentes, portanto, não há transações distribuídas.

A maneira de alcançar essa bondade é identificar primeiro os recursos de negócios. A capacidade comercial é uma responsabilidade comercial específica. Alguma contribuição para o valor geral dos negócios. Então, aqui está a minha sequência de etapas que eu tomo quando penso nos limites do sistema:

  1. Identifique responsabilidades comerciais de nível superior. Haverá alguns deles. Trate esses serviços como uma etapa que sua organização deve seguir para atingir seu objetivo de negócios.
  2. Mergulhe mais fundo em cada serviço. Identifique serviços de nível inferior, incluindo um pai.
  3. Ao lado dos dois primeiros pontos, pense na comunicação de serviço. Eles devem fazer isso principalmente por meio de eventos, apenas para notificar um ao outro sobre o resultado do processo de negócios. Eventos não devem ser considerados como transportadores de dados.

Lembre-se de que o serviço comercial inclui pessoas, aplicativos, processos comerciais. Normalmente, apenas uma parte é representada como autoridade técnica.

Isso pode parecer um pouco abstrato, portanto, provavelmente um exemplo de identificação de limites de serviço seria de algum interesse.

Zapadlo
fonte
0

Apenas outro fator a ser adicionado às respostas atuais. Com serviços de granulação grossa . Você deseja evitar a latência de todas as chamadas; portanto, em vez de fazer 10 chamadas, você faz uma chamada que obtém 10 partes de dados necessárias em um DTO.

E lembre-se de que os microsserviços não são tão micro quanto as pessoas pensam.

Borjab
fonte