Técnicas para Performance e escalabilidade em Microsserviços

Henrique Leite
7 min readOct 30, 2020
Photo by Natalie Pedigo on Unsplash

Durante muito tempo busquei cases de como empresas criam softwares que possam escalar globalmente. E em uma das minhas experiências profissionais eu tive a sorte de aprender e desenvolver técnicas para alcançar aquilo que admiro em empresas como Youtube, Amazon, Facebook, Instagram, Netflix, etc.

Esse artigo é para você que como eu tem estudado técnicas de como alcançar desafios como esses.

Por onde começamos?

Como o projeto já estava construído e em produção as alterações precisavam ser cuidadosas para não ter impacto e tínhamos que definir o que iriamos fazer e como. Fizemos uso do framework Well Architected da AWS, com isso conseguimos definir épicos e atividades e tínhamos um caminho a ser seguido.

Juntamos todas as atividades e montamos Sprints semanais e toda segunda-feira fazíamos uma análise detalhada de quais atividades iriam trazer mais impacto positivo para o usuário, ou seja todas nossas ações eram orientadas a resultados que aumentassem nosso Apdex. Como esse inicialmente era nosso objetivo, então todas as entregas em produção eram medidas, no ambiente de homologação também era comparado o antes x depois.

O Apdex é um padrão aberto para medir o desempenho de aplicativos de software em computação. fonte: https://en.wikipedia.org/wiki/Apdex

Montamos o mapa a seguir das atividades que precisávamos executar para chegar em um produto com características globais e alta performance.

As caixas com vermelho foram as que mais tivemos resultados positivos, mas todas elas consideramos essenciais para um projeto global

Com essa mapa a gente criou algumas perguntas e comentários para nos ajudar no raciocínio e diagnóstico.

Segurança

  • Acessos as API são controlados por token em públicos e api-key para internos?
  • Temos ferramentas como o Sonar integrado ao CI/CD para garantir as regras de segurança, qualidade de código e cobertura de testes?
  • Nossos serviços são idempotentes?
  • Estamos logando informações de header, senhas, payloads com dados de usuários ou dados que possam colocar em risco nossa segurança e dos nossos clientes?

Estabilidade

  • Funcionalidades contém chamadas desnecessárias na camada de front ou backend?
  • A aplicação está com o nível de logs corretos em produção?
  • Os logs do Kibana estão sob controle?
  • Temos tracing das request nos logs?
  • Existe formatação desnecessário em logs pode podem afetar no tempo de performance da aplicação por exemplo Logger.debug(json.stringfy… )?
  • As dependências do projeto estão atualizadas?
  • A aplicação não contém nenhum dependência com vulnerabilidade?
  • Estamos avaliando e tratando os erros 5xx?
  • Nossas queries fazem uso de colunas com índices?
  • Existe índices criado sem migration?
  • Nosso código esta otimizado, em relação a memória e CPU? E o serviço pode estar com algum memory leak?
  • Estamos carregando dados desnecessários para memória?
  • Nossas API fazem uso de paginação?
  • Estamos carregando para memória um resultado muito grande do banco de dados, excel, etc?

Performance

Banco de dados

  • Estamos usando o banco de dados adequado para o negócio?
  • Avaliamos o uso de cache para get, nosql para queries e sql para escritas?
  • O Pool de conexão está configurado considerando o limite do banco em um cenário de auto scalling?

Cache

  • Podemos utilizar camadas de Cache?
  • O cache está em uma classe correta em PRD, avaliando critérios de leituras e escritas?
  • A composição de chave do cache esta adequada?
  • Os dados do cache estão sendo salvos com TTL?
  • Existe um mecanismo de limpeza das chaves menos utilizadas?
  • Existe retry quando as conexões são perdidas entre o pod e o cache/banco?

Read/Write

  • Podemos segregar a leitura da escrita para evitar concorrência com o transacional?

Protocolo de requests

  • Estamos fazendo uso das boas práticas do http1?
  • Podemos usar GRPC ou http2?
  • O servidor http é o mais adequado para serviço, beneficio x performance?

Batch e Online

  • Podemos segregar as transações online de operações batch que ocorrem e concorrem entre no mesmo servidor?
  • As transações batch podem ser migradas para serverless?

Gateway interno

  • As chamadas internas estão utilizando um gateway interno ao invés de público?
  • As permissões de rotas estão adequadas, foi liberado apenas o necessário ou invés de methods=[all]?

Resiliência

Eventos

  • Podemos fazer uso de eventos nesse serviço para evita chamadas http para outros serviços?
  • Nossos eventos estão bem configurados com dead letter, throttling e são bem monitorados?
  • O tempo de manter a mensagem na fila é adequado ao negócio?

Circuit Breaker

  • Podemos fazer uso de Circuit Breaker para aumentar a resiliência do serviço?
  • O negócio permite o uso dessa técnica?
  • Ele esta configurado respeitando o time do ELB e Gateway?
  • Qual será o comportamento da aplicação com o circuito aberto?

Escalabilidade

Expurgo das bases

  • Nossas tabelas estão apenas com os dados transacionais?
  • Facilitamos a pesquisa dos dados quando necessário em tabelas auxiliares de histórico?
  • Durante o expurgo não impactamos a aplicação?

Particionamento

  • O banco pode ser particionado com a regra de negócio aderente?
  • Podemos fazer partições por data, status ou região?

Edge e Hub

  • Esses serviço(API) deve ser Edge ou Hub?
  • Ele é core para aplicação?
  • Existe latência ou manter ele como Edge?
  • Estamos usando cache edge dos estáticos?

Multi AZ

  • Nossa aplicação tem uma réplica em outras regiões?
  • O que ocorre ser perdemos o servidor de uma região?
  • Como iremos replicar os dados e quando?
  • Qual o tempo de recuperação?
  • Qual valor para manter a réplica?

Master e Slave

  • Temos algum servidor que pode ser uma ponto de falha e precisamos de réplicas caso ele falhe?
  • Já testamos a queda do principal e como a aplicação se comporta?
  • O usuário é afetado caso ocorra o redirecionamento?

Blue Green e Cannary Release

  • Usamos técnicas seguras de deploy?
  • Nossas lambdas são versionadas?
  • Nossos deploys são constantes?
  • O que ocorre se uma imagem estiver com erro?
  • O que ocorre se funcionalmente o serviço não estiver correto?
  • Fazemos uso de feature toogle para testar novas funcionalidades?

Multi-region deployment

  • Nossa esteira de CD realiza deploy em mais de uma região para uma estratégia Muti AZ?

Custos

Recursos desnecessários

  • Temos um acompanhamento se o que estamos pagando estamos usando?
  • Realizamos as otimizações necessárias?
  • Os custos são monitorados?
  • Sabemos o custo de cada serviço?

Auto scalling CPU x Request

  • Nosso negócio demanda um HPA por CPU ou Requests?
  • Um excesso de processo no mesmo POD pode ter um gargalo de conexões de rede?

Lambdas x Pods

  • Avaliamos se uma API poderia ser serverless por suas características técnicas e de negócio?
  • Nossas lambdas fazem reuso de conexões?
  • Tenho API em pods que recebem requests com baixa frequência ou que poderiam ser assíncronas?

Além disso também montamos um mapa mental para nos ajudar a seguir um racional.

Como definir as prioridades

No nosso cenário priorizamos atividades que iriam tornar a usabilidade dos usuários mais agradável, sem aqueles "carregando…." ou indisponibilidades nos principais dias de vendas. Então buscamos quick wins e conseguimos encontrar muitas coisas que rapidamente elevaram o nível do nosso Apdex. Conforme elas foram finalizando começamos a trabalhar em temas mais estruturais na arquitetura da aplicação, implementamos camadas de cache, eventos para reduzir chamadas http e mudanças de banco de dados SQL para NoSQL. Todas essas decisões sempre estiveram muito bem alinhadas com negócio, pois não podemos esquecer que a tecnologia é o meio e não o fim.

Também olhamos para para serviços que eram pontos de falhas na arquitetura, que quando caiam geravam um efeito domino. Criamos réplicas e desenvolvemos uma resiliência para quando o serviço estava fora, basicamente é uma réplica dos dados com um tempo de vida e que recebem eventos quando ocorrem alterações.

Nos guiamos em testes de carga para buscar onde estavam os gargalos ou dados de produção usando ferramentas de APM que mostravam no momento de pico de request o que começou a gerar lentidões. Como seguimos esse formato era possível no mesmo dia da entrega em produção já visualizar os ganhos de performance ou estabilidade.

Time

Se eu pudesse definir uma palavra durante esse percurso eu diria "divertido", montamos um time com autonomia , multidisciplinar e colaborativo. Aprendemos com os nossos erros e trocamos experiências, o importante sempre foi tentar melhorar no dia seguinte.

Sabíamos que milhares de pessoas utilizavam nosso produto e era gratificante saber que a cada entrega elas teriam um produto melhor.

Conclusão

É importante realizar provas de conceitos e tomar decisões baseadas em dados, evitar trazer problemas que não existem para o presente. O mínimo que devemos fazer é tomar a decisão certa com os dados que existem no momento e compartilhar com os envolvidos. Evitar ao máximo desenvolver "paliativos" ou conhecido também como "gambiarras", faça o simples mas que tenha segurança, qualidade e que você não precise voltar no mesmo assunto nos próximos meses.

Tome a decisão certa com os dados existentes e mantenha sua arquitetura atualizada na medida que o produto evolui.

Como já disse logo acima, nada disso seria possível sem uma cultura onde o desenvolvimento é reconhecido como uma arte e que as pessoas precisam de liberdade para que possam ser criativas e que elas não tenham medo de errar

Trabalhe com pessoas que expandam seu conhecimento e que juntos ajudem a melhorar a vida de pessoas com o apoio da da tecnologia

Obrigado e espero que esse artigo seja útil e deixe nos comentários dúvidas ou sugestões.

--

--

Henrique Leite

GoBlockchain Founder | Clarisse’s father.. | Married | In Blockchain we trust | Surfer