O que é desempenho?
De acordo com o dicionário Cambridge, desempenho significa: “quão bem uma pessoa, máquina, etc. faz um trabalho ou uma atividade”. Ok, isso explica o significado geral de desempenho, mas como isso afeta a modelagem de uma API, na arquitetura de sistemas e, mais importante, como isso afeta a qualidade do sistema?
Como pode ser medido?
Tomando como medida a definição que foi fornecida anteriormente, o desempenho mede o quão “bem” uma atividade é feita. Mas então como o sucesso de um sistema, de uma API, pode ser medido? O que pode ser usado para um roteiro de padrões a serem criados? Vamos nos concentrar em um aspecto principal do desempenho e uma medição para conquistá-los todos (desculpe pela piada). Latência.
Definição de latência
Primeiro de tudo, uma explicação geral do que é latência, de acordo com o dicionário Cambridge latência é “o atraso entre uma instrução para transferir (= mover) informações do computador e as informações sendo transferidas, por exemplo, pela internet”. Então, basicamente latência é quanto tempo leva para uma ação ser concluída, ou neste caso, em quanto tempo a API responde.
Latência ideal
Agora que a definição de latência está clara, qual é o modelo que deve ser seguido? Como podemos saber se a latência, o tempo de resposta da API, é realmente bom?
Uma pesquisa liderada pela Dra. Gitte Lindgaard descobriu que as pessoas podem tomar decisões difíceis sobre o apelo visual de uma página da web após serem expostas a ela por apenas 50 ms, o que é 1/20 de segundo (50 ms é apenas metade de 0,1 segundo, mas é próximo o suficiente para os propósitos de uma análise de "potências de 10".)
No estudo de Lindgaard, imagens de tela foram exibidas para os participantes do teste por 0,05 segundos, após o que eles puderam distinguir entre designs mais e menos atraentes. É importante perceber que não é assim que os usuários realmente abordam as páginas da web durante o uso real. Por um lado, as páginas não piscam na tela por um instante e depois desaparecem. Em vez disso, elas são renderizadas ao longo de um período de um segundo (se tivermos sorte — caso contrário, mais). Além disso, as pessoas passam alguns segundos olhando a página antes de decidir o que fazer a respeito.
Ainda assim, o estudo mostra que as pessoas podem formar impressões visuais básicas muito rapidamente, nos limites da percepção humana.
0,1 segundo é o limite de tempo de resposta se você quiser que os usuários sintam que suas ações estão causando algo diretamente na tela. Por exemplo, se você clicar em um menu expansível e ver a versão expandida em menos de 0,1 segundo, então parece que você fez o menu abrir. Se demorar mais de 0,1 segundo para o estado revisado aparecer, então a resposta não parece instantânea — em vez disso, parece que o computador está fazendo algo para abrir o menu.
Assim, para criar a ilusão de manipulação direta, uma interface de usuário deve ser mais rápida do que 0,1 segundo.
Em estudos de rastreamento ocular, a maioria das fixações que rastreamos duram pouco mais de 0,1 segundo. Na verdade, a primeira coisa que as pessoas notam ao executar seu primeiro estudo de rastreamento ocular é a rapidez com que o olho humano se move pelas páginas da web (ou outros estímulos). Os usuários olham as coisas muito brevemente, o que é um grande motivo para enfatizar a clareza na usabilidade do conteúdo.
Ok, isso é muito legal, mas quais são os intervalos de tempo em que o tempo de resposta deve passar? Existem 3 limites de tempo principais (que são determinados pelas habilidades perceptivas humanas) para ter em mente ao otimizar o desempenho, e eles são bem novos, estão aqui desde 1968 (sim, você leu certo) [Miller 1968; Card et al. 1991]. São eles:
- 0,1 segundo é o limite para que o usuário sinta que o sistema está reagindo instantaneamente, o que significa que nenhum feedback especial é necessário, exceto para exibir o resultado.
- 1,0 segundo é o limite para que o fluxo de pensamento do usuário permaneça ininterrupto, mesmo que o usuário perceba o atraso. Normalmente, nenhum feedback especial é necessário durante atrasos de mais de 0,1, mas menos de 1,0 segundo, mas o usuário perde a sensação de operar diretamente nos dados.
- 10 segundos é o limite para manter a atenção do usuário focada no diálogo. Para atrasos maiores, os usuários desejarão executar outras tarefas enquanto esperam o computador terminar, então eles devem receber feedback indicando quando o computador espera terminar. O feedback durante o atraso é especialmente importante se o tempo de resposta provavelmente for altamente variável, já que os usuários não saberão o que esperar.
Então, com base nessas informações, o gráfico abaixo mostra o nível de paciência e atenção dos usuários conforme o tempo passa, ou conforme a latência aumenta.
E por que isso é um problema com a arquitetura de microsserviços?
Comparando a arquitetura de microsserviços com um monolito, há uma diferença óbvia, há mais serviços se comunicando entre si, como mostrado na imagem abaixo:
Esse tipo de arquitetura certamente tem muitos benefícios, mas um dos maiores problemas é o desempenho. Mas por quê? Bem, no monolito, todo o processo feito durante uma chamada é feito no mesmo serviço, mas em microsserviços há mais partes envolvidas, mais serviços, isso levou a um problema que às vezes é negligenciado: latência nas integrações. Por exemplo, se sua chamada tem 5 microsserviços envolvidos, e a latência de rede é de apenas 20 ms para cada, há apenas 100 ms de latência aumentada na resposta do cliente. E certamente em uma solução simples isso não é muito, mas em arquiteturas complexas isso começa a se multiplicar exponencialmente e esse problema não pode ser negligenciado.
Resolução
Infelizmente não há uma resposta que resolva esse problema de uma vez por todas, então vamos listar algumas delas:
Entendendo seu software
Seu design de software deve levar em conta o problema que ele está tendo que resolver e qual é a melhor maneira de resolvê-lo. Seja o mais simples que puder, mas sempre projete para o pior cenário.
Usando a melhor ferramenta (linguagem de programação) e algoritmo para o trabalho — otimização
Um dos benefícios dos microsserviços é que um serviço pode ter linguagens de programação diferentes dos outros, então use a melhor ferramenta para o trabalho, não tente resolver diferentes problemas da mesma maneira, eles não são o mesmo problema, por que a mesma solução funcionaria então? E reserve mais tempo para escrever bem os algoritmos, planejar as integrações, sempre tente resolver os problemas da maneira mais simples e inteligente possível.
Cache
O cache é uma ferramenta poderosa que pode fazer toda essa latência de rede cair drasticamente, mas precisa ser usada da melhor maneira e com o TTL (tempo de vida) certo para a solução. Gosto de dividi-los em três tipos:
- Local: Um cache que pode ser criado dentro do próprio serviço, por exemplo, salvando na memória do serviço uma lista de tipos de moeda.
- Distribuído: Um cache que é distribuído dentro de várias instâncias do seu serviço ou até mesmo vários serviços. Normalmente isso se dá quando existe um serviço de cache em que gerencia os dados e outros serviços se integram com ele. Ex: Redis.
- Gateway: Armazenar em cache as respostas dos seus endpoints no lado do gateway, por exemplo, como a AWS dá a possibilidade, ajuda a diminuir a latência e o estresse no seu serviço. O serviço precisa processar as chamadas necessárias, mas não necessariamente todas elas. Para um exemplo concreto, se um serviço retorna o preço de um produto que é alterado diariamente apenas, não há necessidade de cada requisição passar por toda a cadeia de serviço. Armazenar em cache a resposta pode ser a solução.
Claro que o cache não é uma solução barata, então deve ser bem analisado se é realmente necessário e a melhor maneira de usá-lo. Mas uma coisa deve ser levada em conta, vamos supor que um serviço de geração de links de pagamento leve 5 segundos para responder, e isso, como mostrado no gráfico, impacta a atenção e retenção do usuário. Armazenando em cache as integrações, essa latência cairia para 2 segundos, por exemplo, fazendo uma rápida matemática de quanto é perdido devido a essa latência e o quanto isso afeta o UX, a solução de cache pode até se pagar.
Paralelismo
Da mesma forma que a seção "otimização" olhou na seção de codificação, o paralelismo é uma ferramenta poderosa, mas leve em conta que uma ferreamente poderosa tem uma possibilidade de construção gigante, mas o potencial de destruição [e diretamente proporcional. Pode ajudar muito a fazer tarefas usando o máximo de sua estrutura e recursos de hardware, mas tem que ser bem usado e bem pensado, ou pode levar à hardware starvation ou até mesmo racing conditions.
I/O
Este é bem simples, quanto mais I/O o sistema tiver, mais latência ele terá, mas um ponto que é negligenciado e que está incluído aqui é o log. Log e observabilidade são essenciais em todas as soluções, mas logs em excesso afetam drasticamente o desempenho da solução construída. Então, da mesma forma que todas as outras partes da solução, o log devem ser bem projetado e pensado.
Chamadas assíncronas
Não há como resolver as muitas integrações que a arquitetura de microsserviços traz, mas há uma maneira de projetar do melhor modo essas integrações. Usando soluções assíncronas, a latência também cai enormemente. É lógico o raciocínio aqui, se eu não dependo de esperar uma ação para finalizar o meu processo, ele será mais rápido, e para entender a possibilidade de usar chamadas assíncronas podem ser respondidas as seguinte pergunta: é preciso esperar a resposta dessa integração para retornar algo ao cliente? E em uma análise ainda mais profunda: Esse serviço ser feito orientado a eventos?
Segurança: evitando chamadas maliciosas
Usar estratégias para bloquear chamadas maliciosas ou DDoS, por exemplo, no início do fluxo, como um firewall ou AWS Shield, ajuda a reduzir o stress nos serviços, não permitindo que chamadas maliciosas cheguem a toda a cadeia da solução.
Balanceamento de carga
Ter mais de uma instância da solução lidando com as requisições é uma maneira conhecida de aliviar o estresse no sistema. Mas ter um balanceador de carga que sabe para qual instância a chamada deve ir, com base não apenas no round robin, mas também em dados, por exemplo: quantas chamadas a instância está segurando ou como a infraestrutura da instância está se comportando, pode ter um grande impacto no desempenho.
Rate Limiting / Throttling
Se a solução tiver um pico que não é conhecido anteriormente, ou não levado em consideração quando o design da solução foi feito, é melhor ter uma rate limit, em outras palavras, é melhor colocar algumas chamadas em espera do que redirecionar todas as chamadas para o sistema. Isso teria um impacto nas chamadas que devem ser retidas e também nas chamadas que estão sendo processadas. Em síntese, é melhor ter uma latência maior em algumas chamadas do que em todas as chamadas.
Monitoramento
Este é um tema negligenciado que simplesmente não pode ser, toda solução tem que ser monitorada. Toda solução deve ter observabilidade. Claro que isso deve ser bem pensado e projetado, pois em excesso o desempenho pode ser afetado, como abordado anteriormente. Mas tracing, logs e alarmes são o mínimo para uma solução sustentável.
Conclusão
Mas finalmente, como fazer uma solução ter um bom desempenho? A resposta é o famoso "depende". Existem muitas possibilidades e maneiras, mas um ponto é permeado: Construa e analise sua solução visando o que ela deve fazer e como fazê-lo melhor. Não use uma solução porque ela vai resolver todos os problemas, estude o sistema, estude a solução, projete e analise o custo dela. Mas acima de tudo isso, o design da solução é vivo e deve ser atualizado junto com seus requisitos e funcionalidades. Independente das ferramentas escolhidas elas devem ser usadas da maneira mais adequada, simples e inteligente possível. Não tente usar uma chave de fenda como um martelo, às vezes pode até funcionar, mas você sofrerá muito mais para chegar no seu objetivo e pode até se machucar.
Referências
Lindgaard, G. , Fernandes, G., Dudek, C. and Brown, J. (2006) Attention web designers: You have 50 milliseconds to make a good
first impression! — Behaviour & Information Technology, Vol. 25, №2
Miller, R. B. (1968). Response time in man-computer conversational transactions. Proc. AFIPS Fall Joint Computer Conference Vol. 33, 267–277.
Nielsen, J. (1993). Usability Engineering — Chapter 5
Top comments (0)