O pilar de desempenho concentra-se na concepção, desenvolvimento, validação e operação de soluções que atendam aos seus requisitos não funcionais de desempenho (normalmente associados a tempos de resposta), capacidade (magnitude da carga suportada, base de usuários e taxas de transferência alcançadas) e escalabilidade (capacidade de acomodar organicamente a demanda variável e o aumento de cargas). Ao contrário de um ambiente de computação "tradicional" composto por infraestrutura de capacidade fixa, um ambiente de nuvem híbrida permite que as soluções escalem dinamicamente sua capacidade e consumo de recursos conforme a demanda aumenta ou diminui, desde que as soluções sejam arquitetadas para aproveitar esses recursos.
A análise de desempenho também permite melhorar a experiência do usuário por meio de melhorias baseadas em evidências do design do produto e atingir as metas de negócios por meio da escalabilidade e da capacidade integradas.
Na fase de definição do produto, reúna as expectativas dos usuários, quantifique os requisitos de negócio e utilize-os como base para a arquitetura e o design do produto posteriores.
Os componentes dentro de uma solução bem arquitetada podem ser escalados de maneira independente, por exemplo, adicionando outra instância de um serviço, mas adicionar ou remover um componente pode ter efeitos em cascata em outras partes da solução; por exemplo, adicionar outro servidor web para atender a um pico de tráfego web pode exigir mais filas de mensagens para se comunicar com os serviços de back-end. Conhecer as dependências de escalabilidade antecipadamente pode ajudar a compreender o comportamento operacional da solução e a evitar o esgotamento de recursos devido à escalabilidade excessiva de um único recurso.
Soluções de nuvem híbrida bem arquitetadas aproveitam as arquiteturas multiplataforma para escalar e expandir as estratégias para otimizar o desempenho, bem como a segurança, o custo operacional e as expectativas do usuário final; por exemplo, executar cargas de trabalho na infraestrutura local com fortes garantias de desempenho e custos operacionais fixos e aumentar/escalar para um provedor de serviço de nuvem pública durante períodos de pico da carga de trabalho.
A migração de dados é cara. Soluções bem arquitetadas aproveitam a portabilidade e a mobilidade dos serviços de cargas de trabalho conteinerizados, posicionando-os o mais próximo possível dos dados que consomem.
As soluções devem escolher a plataforma e os recursos certos para maximizar o valor de suas arquiteturas. Uma solução de nuvem híbrida é capaz de abranger múltiplas nuvens, incluindo a infraestrutura no local, o que dá aos arquitetos a liberdade de selecionar a combinação ideal de recursos para atender às necessidades de desempenho de sua solução.
O desempenho deve ser "integrado" a uma solução desde o início de seu projeto. Deixar as considerações de desempenho para o final do projeto da solução, ou pior, para a implementação, geralmente resulta em um desempenho abaixo do ideal, que não pode ser corrigido sem revisar grande parte da arquitetura da solução. As práticas de design das soluções ajudam os arquitetos a criar soluções de alto desempenho e a evitar abordagens de design que possam limitar o desempenho da solução.
As soluções devem ser projetadas para aumentar ou diminuir sua capacidade de processamento adicionando ou removendo unidades discretas (servidores, serviços, interfaces de rede etc.), em vez de alterar a capacidade das unidades existentes, por exemplo, adicionando mais CPUs a um servidor. Para conseguir isso, as soluções devem adotar os seguintes princípios de arquitetura:
Componentes sem estado são componentes que não retêm o estado do cliente ou da sessão (por exemplo, a identidade do usuário ou as entradas de dados fornecidas na chamada anterior) entre as interações, eliminando as dependências entre os clientes e qualquer instância específica de um componente. Essa falta de dependência entre os componentes e seus consumidores significa que a solução pode ser escalada para cima ou para baixo, adicionando ou removendo instâncias de componentes, sem impacto para os consumidores dos serviços dos componentes. Um bom exemplo de um componente sem estado é um caixa no supermercado; desde que haja pelo menos um caixa disponível, os clientes podem finalizar suas compras e pagar por elas, e outros caixas podem ser adicionados e removidos conforme a demanda de clientes. O inverso disso seria se os clientes tivessem um caixa específico designado no início de suas compras. Se o caixa designado ficar sobrecarregado ou, pior, indisponível, os clientes terão que esperar ou começar tudo de novo, e o desempenho geral do supermercado (medido em clientes por hora) será prejudicado.
Evite tarefas de longa duração. Se uma solução precisar suportar tarefas de longa duração (por exemplo, realizar um cálculo científico complexo), a tarefa deverá ser projetada para suportar o aumento ou redução da escalabilidade por meio de uma instalação de interrupção e ponto de verificação que permita que a solução desligue e reinicie a tarefa à medida que recursos são adicionados e removidos da solução.
Dados nas extremidades. Os componentes sem estado podem, em teoria, escalar infinitamente e ser reutilizáveis entre clientes. Soluções de alto desempenho transferem o estado, ou seja, os dados do usuário e da aplicação, para a aplicação cliente e os bancos de dados nas extremidades da arquitetura da solução, não mantendo nenhum estado nas camadas intermediárias da arquitetura.
Recursos:
Com estado vs. Sem estado
Projetar soluções como um conjunto de componentes altamente coesivos e fracamente acoplados permite que os componentes sejam escalonados de maneira independente em relação à demanda pelo serviço que prestam. Abordagens de arquitetura como a arquitetura orientada a serviços e os microsserviços incorporam essa prática como um princípio de design fundamental, ou seja, um conjunto de serviços altamente coesivos que se comunicam por meio de APIs de alto nível e fracamente acopladas.
A transferência de dados entre os componentes de uma solução costuma ser o elemento mais demorado de uma transação. Os componentes devem ser projetados para otimizar a frequência e o volume de comunicações de acordo com a largura de banda disponível. Por exemplo, uma aplicação que faz chamadas repetidas para recuperar valores individuais de um banco de dados pode ter um desempenho "suficientemente bom" quando implementada em uma rede local, mas pode apresentar atrasos quando o componente do banco de dados é realocado para um provedor de serviço de nuvem.
O estilo arquitetônico de transferência de estado representacional (REST), comumente utilizado em aplicações baseadas na web, é um bom exemplo do tipo de equilíbrio exibido por uma solução bem arquitetada; o estado representativo completo de um recurso é transferido como JSON, XML ou outro documento que equilibra a quantidade de informações transferidas com a alta latência de uma interação baseada na web.
O armazenamento em cache ajuda a limitar a demanda sobre os recursos e serviços que produzem dados. Considere usar o cache para dados de longa duração, relativamente estáticos e/ou dados cuja produção seja "cara". Soluções bem arquitetadas implementam mecanismos de cache em todas as camadas da arquitetura da solução, posicionando os caches o mais próximo possível do consumidor, de forma lógica, para limitar a comunicação entre consumidor e cache e melhorar o tempo de resposta geral.
Os arquitetos precisam ter em mente que o uso excessivo de cache pode ser prejudicial. Um mecanismo de cache mal projetado ou um cache excessivamente grande podem afetar negativamente o desempenho geral da solução. Os arquitetos devem avaliar o tipo e a estratégia de cache e, em seguida, medir a eficácia do cache durante os testes e análises de desempenho.
A troca de mensagens assíncronas, usando filas de mensagens, modelos de retorno de chamada ou outros meios, permite que as soluções sejam escaláveis de forma eficiente e que se degradem de maneira controlada sob a carga, caso os recursos se esgotem. Soluções bem arquitetadas aproveitam a comunicação assíncrona, particularmente as filas de mensagens, para proporcionar aos usuários finais uma experiência ágil e evitar a perda de solicitações de usuário caso algum componente falhe. Esse mesmo mecanismo também pode ser usado para interconectar sistemas que possuem diferentes níveis de serviço ou horários de funcionamento; por exemplo, uma aplicação web que funciona 24 horas por dia, 7 dias por semana, conectada a um sistema de registro que funciona das 9h às 17h com uma fila de mensagens, permite que a aplicação web aceite solicitações do usuário final mesmo quando o sistema de registro estiver indisponível.
As soluções mudam com o tempo e seu desempenho pode mudar junto com elas. Incorporar ferramentas de instrumentação de desempenho que permitam às equipes de desenvolvimento, teste e operações coletar métricas de desempenho da aplicação de forma não intrusiva ajuda a desenvolver e testar um produto robusto que utiliza métodos baseados em evidências. A instrumentação também auxilia nos testes funcionais, na análise de defeitos e é uma ferramenta valiosa para manter o desempenho de uma solução e ajudar a identificar as fontes de problemas de desempenho na produção. A instrumentação configurável e não intrusiva oferece suporte ao monitoramento de produtos, garantindo a observabilidade da solução nas operações, apoiando assim as equipes de DevOps e SRE.
O planejamento, teste e análise de desempenho são um conjunto de práticas e abordagens aplicadas às soluções de TI com o objetivo de garantir a qualidade da solução e sua capacidade de atingir os resultados de negócios esperados.
Normalmente, a análise se aplica a atributos de qualidade como desempenho, capacidade, escalabilidade e alguns aspectos da disponibilidade, continuidade de negócios e sustentabilidade em geral da solução. A análise inclui a identificação e quantificação dos requisitos de negócios relacionados à qualidade, design e execução dos testes para recuperar métricas específicas que refletem o desempenho da solução em relação a um conjunto de expectativas, como tempos de resposta, taxas de transferência ou cargas suportadas.
Além disso, em um sentido mais amplo, o escopo de desempenho inclui a análise da capacidade de uma solução, o total de unidades de trabalho que a solução pode atender e sua escalabilidade (quão bem ela responde às mudanças na demanda). A análise de desempenho também serve para comprovar que os produtos permanecem funcionais e estáveis em condições extremas de operação. Espera-se que o objetivo da análise de desempenho não seja apenas capturar o panorama do desempenho da solução, mas também identificar os gargalos e colaborar com os stakeholders para melhorar a qualidade e a usabilidade da solução.
Considerando a natureza complexa e holística da gestão de desempenho e capacidade do produto, ela deve abranger as diferentes fases do SLDC, desde o projeto do produto até o suporte operacional e a SRE. Isso garante o gerenciamento adequado dos requisitos do cliente, a detecção precoce de problemas e a resposta rápida aos incidentes de produção.
Do ponto de vista de negócios, o desempenho da solução como um todo é importante, e isso deve se refletir em requisitos não funcionais abrangentes. No entanto, para os testes de desempenho em nível unitário, os testes de desempenho iniciais que implementam o paradigma "shift-left" e para a análise da causa raiz de problemas de desempenho, pode ser necessário especificar os requisitos de baixo nível, limitando as durações das chamadas individuais, a latência da rede e assim por diante.
Portanto, os requisitos de desempenho de alto nível são normalmente fornecidos no nível do processo ou da transação, por exemplo, "O processo de concessão de empréstimo deve ser concluído em menos de 2 minutos", sem levar em consideração como o desempenho das etapas e subprocessos individuais do processo contribui para os resultados finais. Criar um orçamento de desempenho que aloque metas para cada etapa do processo na fase de desenvolvimento fornece objetivos mensuráveis para as equipes de desenvolvimento de funcionalidades, ajuda a identificar possíveis áreas problemáticas e a concentrar os esforços de otimização e remediação de desempenho onde eles são mais benéficos.
Os orçamentos de desempenho devem considerar todas as camadas da solução, desde o hardware até o código da aplicação. Deixar de lado qualquer um desses elementos pode impedir que a solução atenda às expectativas do usuário.
Quando o assunto é testar o desempenho, o que vale é a prática: só testando a solução de ponta a ponta é que dá para ter certeza de que ela atende aos requisitos. Isso reflete a natureza holística da análise de desempenho. Os testes de componentes individuais (por exemplo, banco de dados, middleware, etc.) fornecem insights valiosos sobre a análise de desempenho de uma solução e ajudam a identificar os gargalos de desempenho. Mas o teste de componente por si só não é suficiente, pois as interações entre os componentes podem levar a gargalos inesperados ou outros impedimentos que podem resultar em resultados abaixo do ideal.
Focar na percepção do usuário significa focar principalmente nos tempos de resposta percebidos e na capacidade de resposta geral da interface do usuário. A degradação da capacidade geralmente é invisível para os usuários até que o desempenho do produto seja afetado. Um estudo de 1968 descobriu que há três ordens de magnitude distintas nas interações humano-computador:
A partir disso, concluiu-se que um tempo de resposta de 2 segundos seria ideal e, portanto, 2 segundos ou um pouco mais é uma boa meta de tempo de resposta para soluções de nuvem híbrida, sempre que possível. É claro que as expectativas dos usuários dependem do que eles estão fazendo; por exemplo, ninguém espera que a animação de um botão dure 2 segundos, mas 2 a 3 segundos é uma boa meta geral para aplicativos voltados para o usuário.
Partindo desse princípio, as soluções devem incorporar testes de tempo de resposta do usuário como parte de seu ciclo de garantia de qualidade, utilizando ferramentas de teste de interface do usuário. Embora a latência das chamadas à API seja importante para o desempenho geral do produto, o desempenho percebido pelo usuário é fundamental para atrair e fidelizar a base de usuários.
Os usuários geralmente têm expectativas diferentes sobre o que constitui um "bom" desempenho. Por exemplo, um "usuário avançado" que usa uma aplicação várias vezes ao dia diariamente tem expectativas de desempenho muito diferentes de alguém que usa a mesma aplicação talvez uma vez por mês. Os usuários também costumam ter dificuldade em quantificar o que um "bom" desempenho significa para eles, muitas vezes ficando presos a requisitos como "rápido o suficiente" (que é uma meta difícil de atingir). Além disso, a percepção individual do desempenho do produto para o usuário varia de pessoa para pessoa. Por exemplo, para alguns, um tempo de login de 10 segundos é aceitável (especialmente se for um evento único), para outros pode ser muito lento (especialmente se o login for uma parte frequente do fluxo de trabalho).
Para ajudar a quantificar e gerenciar as expectativas do usuário, recomenda-se:
Monitore e comunique os limites de desempenho aos clientes.
O uso indevido ou a configuração incorreta de um produto ou componente da solução pode ser uma fonte de desempenho insatisfatório e experiência negativa para o usuário. Para evitar essa situação, os arquitetos devem:
Estar cientes das limitações de desempenho da solução e comunicá-las proativamente aos usuários. Por exemplo, se uma solução usar um canal de comunicação lento/de baixa largura de banda, o arquiteto deve conscientizar os usuários finais de que o download de imagens de alta resolução será prejudicado.
Permitir que os sistemas detectem e comuniquem quando as solicitações estiverem fora dos parâmetros de projeto da solução, sempre que possível. Por exemplo, configure o sistema para alertar ativamente os usuários quando eles tentarem baixar arquivos grandes por uma conexão lenta.
Uma abordagem comum para testes de desempenho é verificar se a solução atende às metas de tempo de resposta na carga máxima esperada; a premissa é que, se a solução tiver um bom desempenho na carga máxima esperada, ela também terá um bom desempenho com cargas menores. O problema dessa abordagem é que ela fornece apenas um ponto de dados por carga máxima testada, quase como um exercício de aprovação/reprovação.
Uma solução bem arquitetada é testada usando a abordagem exploratória para verificar sua capacidade de resposta a uma variedade de cargas, variando em tamanho, tipos de usuários e funções testadas. Isso fornece à equipe de solução informações multifacetadas valiosas sobre como os componentes da solução interagem para afetar o desempenho, possíveis gargalos e como escalar a solução para lidar com cargas de trabalho menos ou maiores.
Essa abordagem permite evitar testes adicionais caso as metas de carga esperadas mudem e haja necessidade de coletar métricas de desempenho sob diferentes condições de carga. A inter/extrapolação das dependências existentes do desempenho em relação às magnitudes da carga permite calcular as métricas de desempenho para qualquer carga dentro do espectro das cargas inicialmente testadas (de zero até as magnitudes do ponto de ruptura e acima).
A abordagem típica para testes de desempenho segue o padrão simplificado de "medir os tempos de resposta em determinadas cargas/taxas de transferência". Essa abordagem responde à questão de saber se o tempo de resposta das principais transações nas magnitudes de pico atende aos acordos de nível de serviço (SLAs) existentes. E normalmente as magnitudes testadas são limitadas a "baixa", "pico" e "estresse". Essa abordagem pode responder às perguntas sobre os tempos de resposta em cargas típicas, mas não oferece uma visão completa do desempenho do sistema em todas as situações possíveis.
A abordagem mais avançada de *teste exploratório de desempenho* visa a criação do *instantâneo de desempenho* da solução testada. O instantâneo inclui um conjunto abrangente de métricas de desempenho, coletadas na mais ampla gama de cargas suportadas, desde medições de usuário único até cargas pós-ponto de ruptura (quando possível, exceto quando o sistema trava). Isso inclui uma coleção de dados sobre os tempos de resposta das transações, taxas de transferência dos dados das transações e dados de consumo de recursos coletados em condições de carga crescentes. Por "transação", entendemos uma tarefa específica realizada pelo sistema, desde macrotransações como login ou atualização da conta, até subtransações individuais (como uma chamada de autenticação dentro da transação de login) ou simples acessos de HTTP.
O conjunto abrangente de dados de desempenho, o instantâneo de desempenho, inclui as métricas de desempenho acima para a faixa de carga "linear" de baixa carga (onde os encadeamentos processados individualmente não interagem entre si e os tempos de resposta não aumentam com o aumento da carga), a faixa "não linear", onde os tempos de resposta aumentam conforme a carga aumenta, os pontos de saturação, onde a taxa de transferência para de aumentar com o crescimento da carga e atinge os níveis de saturação, e a faixa pós-ponto de ruptura, onde o desempenho diminui após a taxa de transferência atingir o máximo e os tempos de resposta excederem os níveis de SLA.
Abranger toda a gama de cargas geralmente não exige mais esforço das equipes de teste do que simplesmente testar as magnitudes de "pico" e "estresse" e os testes de resistência, já que os mesmos scripts de teste são usados (e o principal esforço geralmente é criar esses scripts). Mas as vantagens da criação desse instantâneo de desempenho são as seguintes:
Não é necessário gastar tempo e esforço tentando adivinhar a configuração de teste correta que produza a taxa de transferência de transações "correta" ("pico" ou "estresse"), basta aumentar sua carga e abranger *todas* as magnitudes de carga e taxas de transferência suportadas.
É possível determinar em que níveis de carga os tempos de resposta começam a aumentar, onde excedem os níveis de SLA e onde a taxa de transferência atinge os níveis máximos.
Isso permite medir diretamente a capacidade do sistema, por exemplo, a magnitude da carga na qual a condição de ponto de ruptura é atingida (o tempo de resposta excede o SLA, ou as taxas de transferência atingem os níveis máximos, ou o uso de recursos do sistema entra na "zona vermelha" especificada, por exemplo, o uso da CPU chega a 90%, ou o sistema trava, o que ocorrer primeiro). Isso significa que não há necessidade de usar as abordagens típicas baseadas em palpites para a análise e o planejamento de capacidade.
Caso as condições esperadas das operações de produção mudem (por exemplo, as cargas médias e de pico esperadas sejam redefinidas pela empresa), não há necessidade de executar novamente os testes de desempenho: a métrica de desempenho desejada para as diferentes magnitudes de carga pode ser obtida simplesmente por interpolação ou extrapolação dos resultados dos testes existentes.
Abranger todo o espectro de cargas, em vez de apenas algumas magnitudes predefinidas, permite garantir que tenhamos uma visão completa do desempenho do sistema e não deixemos passar nenhum possível problema de desempenho por não testar o produto em condições extremas.
Garantir que os testes incluam atingir os limites de desempenho do sistema significa que sabemos onde estão os gargalos de desempenho, qual link, componente ou camada falhará primeiro à medida que as cargas aumentam. Isso permite fornecer um feedback útil e baseado em evidências às equipes de arquitetura e design para melhorar a robustez e o desempenho do produto.
Existem diversos tipos de testes de desempenho que podem ser executados em uma solução. Uma solução bem arquitetada faz uso de todos eles.
Procure tirar o máximo proveito da variedade de dados de desempenho obtidos nos testes: