Resolver problemas de latência elevada no app

Em muitos casos, a latência elevada no aplicativo resultará em erros de servidor 5xx. Portanto, faz sentido seguir um conjunto semelhante de etapas de solução de problemas para restringir a causa raiz dos picos de erro e latência, uma vez que as causas de cada um podem ser as mesmas.

Definir o escopo do problema

Primeiro, defina o escopo do problema da forma mais restrita possível ao coletar informações relevantes. Veja abaixo algumas sugestões de informações que podem ser relevantes.

  • Quais IDs, serviços e versões de aplicativo são afetados?
  • Quais endpoints específicos do app são afetados?
  • Isso afetou todos os clientes ou um subconjunto específico de clientes?
  • Qual é o horário de início e término do incidente? Especifique o fuso horário.
  • Quais erros específicos você está vendo?
  • Qual é o delta de latência observado, que geralmente é especificado como um aumento em um percentil específico? Por exemplo, a latência aumentou em 2 segundos no 90º percentil.
  • Como você mediu a latência? Em particular, ele foi medido no cliente ou está visível no Cloud Logging e/ou nos dados de latência do Cloud Monitoring fornecidos pela infraestrutura de exibição do App Engine?
  • Quais são as dependências do aplicativo e alguma delas teve incidentes?
  • Você fez alterações recentes no código, na configuração ou na carga de trabalho que possam acionar esse problema?

Um aplicativo pode ter o próprio monitoramento e geração de registros personalizados que podem ser usados para restringir ainda mais o escopo do problema, além das sugestões acima. Definir o escopo do problema orientará você na causa raiz provável e determinará as próximas etapas de solução de problemas.

Determinar o que falhou

Em seguida, determine qual componente do caminho de solicitação tem mais probabilidade de causar a latência ou os erros. Os principais componentes no caminho da solicitação são:

Cliente -> Internet -> Google Front End (GFE) -> Infraestrutura de exibição do App Engine -> Instância do aplicativo

Se as informações coletadas na etapa 1 não apontarem para a origem da falha, geralmente você precisa começar analisando a integridade e o desempenho das instâncias do aplicativo.

Uma maneira de determinar se o problema está na instância do seu aplicativo é observar os registros de solicitação do App Engine: se você vir erros de código de status HTTP ou latência elevada nesses registros, isso geralmente significa que o problema está na instância que está executando seu aplicativo.

Há um cenário em que erros elevados e latência nos registros de solicitação não podem ser causados pela instância do aplicativo em si: se o número de instâncias do aplicativo não for escalonado verticalmente para corresponder aos níveis de tráfego, as instâncias podem estar sobrecarregadas, resultando em erros e latência elevados.

Se houver erros ou latência elevado no Cloud Monitoring, geralmente será possível concluir que o problema está no upstream do balanceador de carga, que registra as métricas do App Engine. Na maioria dos casos, isso indica um problema nas instâncias do aplicativo.

No entanto, se você notar erros ou latência elevada no monitoramento de métricas, mas não solicitar registros, uma investigação mais aprofundada poderá ser necessária. Isso pode indicar uma falha na camada de balanceamento de carga ou que as instâncias estão tendo uma falha tão grave que o balanceador de carga não pode encaminhar solicitações para elas. Para diferenciar esses casos, veja os registros de solicitações antes do início do incidente. Se os registros de solicitação mostrarem o aumento da latência antes da falha, isso indica que as próprias instâncias do aplicativo começaram a falhar antes do balanceador de carga interromper o roteamento de solicitações para elas.

Cenários que podem causar incidentes

Veja algumas situações que os usuários encontraram.

Cliente

Mapear um IP do cliente para uma região geográfica

O Google resolve o nome do host do aplicativo do App Engine para o GFE mais próximo do cliente, com base no endereço IP do cliente usado na busca DNS. Se o resolvedor de DNS do cliente não estiver usando o protocolo EDNS0, as solicitações do cliente talvez não sejam roteadas para o GFE mais próximo.

Internet

Conexão de Internet ruim

Execute o seguinte comando no cliente para determinar se o problema é a conectividade de Internet.

$ curl -s -o /dev/null -w '%{time_connect}\n' <hostname>

O valor de time_connect geralmente representa a latência da conexão do cliente com o Google Front End mais próximo. Se essa conexão for lenta, é possível resolver ainda mais o problema usando traceroute para determinar qual salto na rede causa o atraso.

Você pode executar testes de clientes em diferentes localizações geográficas. As solicitações serão encaminhadas automaticamente para o data center do Google mais próximo, que varia de acordo com a localização do cliente.

Clientes com baixa largura de banda

O aplicativo pode estar respondendo rapidamente, mas a resposta pode ser desacelerada por gargalos de rede que fazem com que a infraestrutura de exibição do App Engine não envie pacotes em toda a rede o mais rápido possível.

Google Front End (GFE)

Bloqueio de linha do cabeçalho HTTP/2

Os clientes HTTP/2 que enviam várias solicitações em paralelo podem ter latência elevada devido ao bloqueio de cabeça de linha no GFE. A melhor solução é que os clientes façam upgrade para usar o protocolo QUIC.

Encerramento de SSL em domínios personalizados

O GFE encerra a conexão SSL. É necessário um salto extra para o encerramento de SSL se você estiver usando um domínio personalizado, em vez de um domínio appspot.com. Isso pode adicionar latência em aplicativos executados em algumas regiões.

Infraestrutura de exibição do App Engine

Incidente no serviço

O Google vai postar detalhes de um serviço grave em https://status.cloud.google.com/. O Google faz lançamentos gradualmente, então é improvável que um incidente em todo o serviço afete todas as instâncias de uma só vez.

Escalonamento automático

Escalonar verticalmente o tráfego muito rápido

O escalonamento automático do App Engine pode não escalonar suas instâncias tão rápido quanto o tráfego aumenta, levando a sobrecarga temporária. Normalmente, isso ocorre quando o tráfego não é gerado organicamente pelos usuários finais e, em vez disso, é gerado por um programa de computador. A melhor maneira de resolver é limitar o sistema que gera o tráfego.

Picos de trânsito

Os picos no tráfego podem causar latência elevada nos casos em que um aplicativo com escalonamento automático precisa escalonar verticalmente mais rapidamente do que é possível sem afetar a latência. O tráfego de usuários finais geralmente não gera picos de tráfego frequentes. Nesse caso, investigue o que está causando os picos de tráfego. Se um sistema em lote estiver em execução em intervalos, talvez seja possível suavizar o tráfego ou usar diferentes configurações de escalonamento.

Configurações do escalonador automático

O escalonador automático pode ser configurado com base nas características de escalonamento do aplicativo. Esses parâmetros de escalonamento podem se tornar não ideais em determinados cenários.

Os aplicativos do ambiente flexível do App Engine são escalonados com base no uso da CPU. No entanto, o aplicativo pode se tornar vinculado a E/S durante um incidente, resultando em uma sobrecarga de instâncias com um alto número de solicitações, porque o escalonamento baseado em CPU não ocorre.

As configurações de escalonamento do ambiente padrão do App Engine poderão causar latência se forem definidas de forma muito ousada. Se você vir respostas do servidor com o código de status 500 e a mensagem Request was aborted after waiting too long to attempt to service your request nos registros, significa que a solicitação expirou na fila pendente aguardando uma instância ociosa.

Não use o escalonamento manual do ambiente padrão do App Engine se o aplicativo atender ao tráfego de usuários finais. O escalonamento manual é melhor para cargas de trabalho como filas de tarefas. Talvez você veja um aumento do tempo pendente com o escalonamento manual, mesmo quando provisionar instâncias suficientes.

Não use o escalonamento básico do ambiente padrão do App Engine para aplicativos sensíveis à latência. Esse tipo de escalonamento foi projetado para minimizar os custos às custas da latência.

As configurações de escalonamento padrão do ambiente padrão do App Engine fornecem latência ideal para a maioria dos aplicativos. Se ainda houver solicitações com tempo pendente alto, especifique um número mínimo de instâncias. Se você ajustar as configurações de escalonamento para reduzir custos minimizando as instâncias inativas, correrá o risco de ver picos de latência se a carga aumentar repentinamente.

Recomendamos que você compare o desempenho com as configurações de escalonamento padrão e execute uma nova comparação após cada alteração nessas configurações.

Implantações

A latência elevada logo após uma implantação indica que você não aumentou suficientemente antes de migrar o tráfego. Instâncias mais recentes podem não ter aquecido caches locais e, portanto, podem ser exibidas mais lentamente do que instâncias mais antigas.

Para evitar picos de latência, não implante um aplicativo do App Engine usando o mesmo nome de versão de uma versão atual do aplicativo. Se você reutilizar um nome de versão existente, não será possível migrar lentamente o tráfego para a versão nova. As solicitações podem ser mais lentas porque cada instância será reiniciada em um curto período. Também será preciso reimplantar se você quiser reverter para a versão anterior.

Instância do aplicativo

Código do aplicativo

Os problemas no código do aplicativo podem ser muito difíceis de depurar, especialmente se eles forem intermitentes ou não forem facilmente reproduzidos. Para ajudar a diagnosticar problemas, recomendamos que seu aplicativo seja instrumentado usando geração de registros, monitoramento e rastreamento. É possível usar o Cloud Profiler para diagnosticar problemas. Veja este exemplo de diagnóstico da latência da solicitação de carregamento usando o Cloud Trace para fazer upload de mais informações de tempo para cada solicitação.

Também é possível reproduzir o problema em um ambiente de desenvolvimento local, o que pode permitir a execução de ferramentas de depuração específicas da linguagem que talvez não possam ser executadas no App Engine.

Se você estiver executando o ambiente flexível do App Engine, execute o SSH em uma instância e faça um despejo de linha de execução para ver o estado atual do aplicativo. Tente reproduzir o problema em um teste de carga ou executando o aplicativo localmente. Você pode aumentar o tamanho da instância para ver se isso resolve o problema. Por exemplo, o aumento da RAM pode resolver problemas para aplicativos que estão atrasando atrasos devido à coleta de lixo.

Para entender melhor como seu app falha e quais gargalos ocorrem, é possível carregar seu aplicativo de teste até a falha. Defina uma contagem máxima de instâncias e aumente a carga gradualmente até o aplicativo falhar.

Se o problema de latência estiver relacionado à implantação de uma nova versão do código do aplicativo, será possível reverter para determinar se a nova versão causou o incidente. Se você implantar continuamente, talvez tenha implantações frequentes com frequência, que é difícil determinar se a implantação causou o incidente com base no horário de início.

O aplicativo pode armazenar configurações no Datastore ou em outro lugar. Será útil criar uma linha do tempo de mudanças de configuração para determinar se alguma delas se alinha com o início de latência alta.

Mudança de carga de trabalho

Uma alteração de carga de trabalho pode causar latência elevada. Algumas métricas de monitoramento que podem indicar que a carga de trabalho mudou incluem qps, bem como uso ou latência da API. Também é possível verificar se há alterações nos tamanhos de solicitação e resposta.

Falhas na verificação de integridade

O balanceador de carga do ambiente flexível do App Engine interromperá o roteamento de solicitações para instâncias que não passam nas verificações de integridade. Isso pode aumentar a carga em outras instâncias, possivelmente resultando em uma falha em cascata. Os registros do Nginx do ambiente flexível do App Engine mostram instâncias que falham nas verificações de integridade. Analise seus registros e monitoramento para determinar por que a instância não está íntegra ou configure as verificações de integridade para ser menos sensível a falhas transitórias. Observe que haverá um pequeno atraso antes que o balanceador de carga pare de rotear o tráfego para uma instância não íntegra. Esse atraso poderá causar um pico de erro se o balanceador de carga não puder repetir solicitações.

O ambiente padrão do App Engine não usa verificações de integridade.

Pressão da memória

Se o monitoramento mostrar um padrão de serrilhado sobre o uso da memória ou uma queda no uso da memória que se relaciona com as implantações, os problemas de desempenho poderão ser causados por um vazamento de memória. Um vazamento de memória pode causar coleta de lixo frequente, levando a uma latência maior. Provisionar instâncias maiores com mais memória pode resolver o problema se você não conseguir rastreá-lo facilmente para um problema no código.

Vazamento de recursos

Se uma instância do seu aplicativo mostrar uma latência crescente que se correlaciona à idade da instância, é possível que haja um vazamento de recursos que causa problemas de desempenho. Nesse tipo de problema, você também verá quedas de latência logo após uma implantação. Por exemplo, uma estrutura de dados que fica mais lenta ao longo do tempo devido ao maior uso da CPU pode diminuir a velocidade da carga de trabalho vinculada à CPU.

Otimização de código

Algumas maneiras de otimizar o código no App Engine para reduzir a latência:

  • Trabalho off-line: use o Cloud Tasks para que as solicitações do usuário não bloqueiem a espera de conclusão do trabalho, como enviar e-mails.

  • Chamadas de API assíncronas: verifique se o código não está bloqueado aguardando a chamada de API ser concluída. Bibliotecas como o ndb oferecem suporte integrado para isso.

  • Chamadas de API em lote: a versão em lote de chamadas de API geralmente é mais rápida do que enviar chamadas individuais.

  • Desnormalizar modelos de dados: reduza a latência das chamadas feitas para a camada de persistência de dados desnormalizando os modelos de dados.

Dependências

É possível monitorar as dependências do aplicativo para detectar se os picos de latência estão relacionados a uma falha de dependência.

Um aumento na latência de uma dependência pode ser causado por uma mudança na carga de trabalho, bem como por um aumento no tráfego.

Dependência sem escalonamento

Se a dependência não for escalonada à medida que o número de instâncias do App Engine aumentar, a dependência poderá ficar sobrecarregada quando o tráfego aumentar. Um exemplo de uma dependência que pode não ser escalonada é um banco de dados SQL. Um número maior de instâncias de aplicativos resultará em um número maior de conexões de banco de dados, o que pode causar falhas em cascata impedindo que o banco de dados seja inicializado.

Uma maneira de se recuperar disso é a seguinte:

  1. Implantar uma nova versão padrão que não se conecta ao banco de dados.
  2. Encerre a versão padrão anterior.
  3. Implantar uma nova versão não padrão que se conecte ao banco de dados.
  4. Migre lentamente o tráfego para a nova versão.

Uma possível medida preventiva é projetar seu aplicativo para soltar solicitações para a dependência usando a Limitação adaptativa.

Falha na camada de cache

Uma boa maneira de acelerar as solicitações é usar várias camadas de armazenamento em cache:

  • Armazenamento em cache próximo dos usuários finais
  • Memcache
  • Memória na instância

Um aumento repentino na latência pode ser causado por uma falha em uma dessas camadas de armazenamento em cache. Por exemplo, uma limpeza do Memcache pode fazer com que mais solicitações sejam enviadas para o Datastore mais lento.