Cenário de ajuste de desempenho: vários serviços de back-endPerformance tuning scenario: Multiple backend services

Este artigo descreve como uma equipe de desenvolvimento usou métricas para encontrar afunilamentos e melhorar o desempenho de um sistema distribuído.This article describes how a development team used metrics to find bottlenecks and improve the performance of a distributed system. O artigo se baseia no teste de carga real que fizemos para um aplicativo de exemplo.The article is based on actual load testing that we did for a sample application. O código do aplicativo está disponível no GitHub, juntamente com o projeto de teste de carga do Visual Studio usado para gerar os resultados.The application code is available on GitHub, along with the Visual Studio load test project used to generate the results.

Este artigo faz parte de uma série. Leia a primeira parte aqui.This article is part of a series. Read the first part here.

Cenário: chamar vários serviços de back-end para recuperar informações e, em seguida, agregar os resultados.Scenario: Call multiple backend services to retrieve information and then aggregate the results.

Esse cenário envolve um aplicativo de entrega drone.This scenario involves a drone delivery application. Os clientes podem consultar uma API REST para obter suas informações de fatura mais recentes.Clients can query a REST API to get their latest invoice information. A fatura inclui um resumo das entregas, dos pacotes e da utilização total do drone do cliente.The invoice includes a summary of the customer's deliveries, packages, and total drone utilization. Esse aplicativo usa uma arquitetura de microserviços em execução no AKS (serviço kubernetes do Azure) e as informações necessárias para a fatura são distribuídas entre vários microserviços.This application uses a microservices architecture running on Azure Kubernetes Service (AKS), and the information needed for the invoice is spread across several microservices.

Em vez de o cliente chamar cada serviço diretamente, o aplicativo implementa o padrão de agregação de gateway .Rather than the client calling each service directly, the application implements the Gateway Aggregation pattern. Usando esse padrão, o cliente faz uma única solicitação para um serviço de gateway.Using this pattern, the client makes a single request to a gateway service. O gateway, por sua vez, chama os serviços de back-end em paralelo e, em seguida, agrega os resultados em uma única carga de resposta.The gateway in turn calls the backend services in parallel, and then aggregates the results into a single response payload.

Diagrama mostrando o padrão de agregação de gateway

Teste 1: desempenho da linha de baseTest 1: Baseline performance

Para estabelecer uma linha de base, a equipe de desenvolvimento começou com um teste de carga de etapas, aumentando a carga de um usuário simulado até 40 usuários, durante uma duração total de 8 minutos.To establish a baseline, the development team started with a step-load test, ramping the load from one simulated user up to 40 users, over a total duration of 8 minutes. O gráfico a seguir, extraído do Visual Studio, mostra os resultados.The following chart, taken from Visual Studio, shows the results. A linha roxa mostra a carga do usuário e a linha laranja mostra a taxa de transferência (média de solicitações por segundo).The purple line shows the user load, and the orange line shows throughput (average requests per second).

Grafo de resultados de teste de carga do Visual Studio

A linha vermelha ao longo da parte inferior do gráfico mostra que nenhum erro foi retornado para o cliente, o que está incentivando.The red line along the bottom of the chart shows that no errors were returned to the client, which is encouraging. No entanto, a taxa de transferência média atinge o pico de meio caminho pelo teste e, em seguida, é desligada para o restante, mesmo que o carregamento continue a aumentar.However, the average throughput peaks about half way through the test, and then drops off for the remainder, even while the load continues to increase. Isso indica que o back-end não é capaz de acompanhar.That indicates the back end is not able to keep up. O padrão visto aqui é comum quando um sistema começa a atingir os limites de recursos — depois de atingir um máximo, a taxa de transferência realmente cai significativamente.The pattern seen here is common when a system starts to reach resource limits — after reaching a maximum, throughput actually falls significantly. A contenção de recursos, os erros transitórios ou um aumento na taxa de exceções podem contribuir para esse padrão.Resource contention, transient errors, or an increase in the rate of exceptions can all contribute to this pattern.

Vamos nos aprofundar nos dados de monitoramento para saber o que está acontecendo dentro do sistema.Let's dig into the monitoring data to learn what's happening inside the system. O próximo gráfico é extraído de Application Insights.The next chart is taken from Application Insights. Ele mostra as durações médias das chamadas HTTP do gateway para os serviços de back-end.It shows the average durations of the HTTP calls from the gateway to the backend services.

Grafo de durações de chamada HTTP

Esse gráfico mostra que uma operação em particular, GetDroneUtilization , leva muito mais tempo em média — por uma ordem de magnitude.This chart shows that one operation in particular, GetDroneUtilization, takes much longer on average — by an order of magnitude. O gateway faz essas chamadas em paralelo, portanto, a operação mais lenta determina quanto tempo leva para que toda a solicitação seja concluída.The gateway makes these calls in parallel, so the slowest operation determines how long it takes for the entire request to complete.

Claramente, a próxima etapa é examinar a GetDroneUtilization operação e procurar qualquer afunilamento.Clearly the next step is dig into the GetDroneUtilization operation and look for any bottlenecks. Uma possibilidade é o esgotamento de recursos.One possibility is resource exhaustion. Talvez esse serviço de back-end específico esteja ficando sem CPU ou memória.Perhaps this particular backend service is running out of CPU or memory. Para um cluster AKS, essas informações estão disponíveis no portal do Azure por meio do recurso Azure monitor para contêineres .For an AKS cluster, this information is available in the Azure portal through the Azure Monitor for containers feature. Os gráficos a seguir mostram a utilização de recursos no nível do cluster:The following graphs show resource utilization at the cluster level:

Grafo de utilização de nó AKS

Nesta captura de tela, os valores médio e máximo são mostrados.In this screenshot, both the average and maximum values are shown. É importante observar mais do que apenas a média, porque a média pode ocultar picos nos dados.It's important to look at more than just the average, because the average can hide spikes in the data. Aqui, a utilização média da CPU permanece abaixo de 50%, mas há alguns picos de 80%.Here, the average CPU utilization stays below 50%, but there are a couple of spikes to 80%. Isso está perto da capacidade, mas ainda dentro das tolerâncias.That's close to capacity but still within tolerances. Outra coisa está causando o afunilamento.Something else is causing the bottleneck.

O próximo gráfico revela o verdadeiro culpado.The next chart reveals the true culprit. Este gráfico mostra códigos de resposta HTTP do banco de dados de back-end do serviço de entrega, que nesse caso é Cosmos DB.This chart shows HTTP response codes from the Delivery service's backend database, which in this case is Cosmos DB. A linha azul representa os códigos de êxito (HTTP 2xx), enquanto a linha verde representa os erros HTTP 429.The blue line represents success codes (HTTP 2xx), while the green line represents HTTP 429 errors. Um código de retorno HTTP 429 significa que Cosmos DB está temporariamente limitando as solicitações, pois o chamador está consumindo mais RU (unidades de recursos) do que o provisionado.An HTTP 429 return code means that Cosmos DB is temporarily throttling requests, because the caller is consuming more resource units (RU) than provisioned.

Grafo de solicitações limitadas

Para obter mais informações, a equipe de desenvolvimento usou Application Insights para exibir a telemetria de ponta a ponta para uma amostra representativa de solicitações.To get further insight, the development team used Application Insights to view the end-to-end telemetry for a representative sample of requests. Aqui está uma instância:Here is one instance:

Captura de tela de exibição de transação de ponta a ponta

Essa exibição mostra as chamadas relacionadas a uma única solicitação de cliente, juntamente com informações de tempo e códigos de resposta.This view shows the calls related to a single client request, along with timing information and response codes. As chamadas de nível superior são do gateway para os serviços de back-end.The top-level calls are from the gateway to the backend services. A chamada para GetDroneUtilization é expandida para mostrar chamadas para dependências externas — nesse caso, para Cosmos DB.The call to GetDroneUtilization is expanded to show calls to external dependencies — in this case, to Cosmos DB. A chamada em vermelho retornou um erro HTTP 429.The call in red returned an HTTP 429 error.

Observe a lacuna grande entre o erro HTTP 429 e a próxima chamada.Note the large gap between the HTTP 429 error and the next call. Quando a biblioteca de cliente do Cosmos DB recebe um erro HTTP 429, ela automaticamente faz o logoff e aguarda a operação novamente.When the Cosmos DB client library receives an HTTP 429 error, it automatically backs off and waits to retry the operation. O que essa exibição mostra é que durante o 672 MS, essa operação levou, a maior parte desse tempo foi gasta aguardando para tentar novamente Cosmos DB.What this view shows is that during the 672 ms this operation took, most of that time was spent waiting to retry Cosmos DB.

Aqui está outro grafo interessante para essa análise.Here's another interesting graph for this analysis. Ele mostra o consumo de RU por partição física versus RUs provisionada por partição física:It shows RU consumption per physical partition versus provisioned RUs per physical partition:

Grafo de consumo de RU por partição

Para fazer sentido desse grafo, você precisa entender como o Cosmos DB gerencia partições.To make sense of this graph, you need to understand how Cosmos DB manages partitions. As coleções no Cosmos DB podem ter uma chave de partição.Collections in Cosmos DB can have a partition key. Cada valor de chave possível define uma partição lógica dos dados dentro da coleção.Each possible key value defines a logical partition of the data within the collection. Cosmos DB distribui essas partições lógicas em uma ou mais partições físicas .Cosmos DB distributes these logical partitions across one or more physical partitions. O gerenciamento de partições físicas é manipulado automaticamente pelo Cosmos DB.The management of physical partitions is handled automatically by Cosmos DB. À medida que você armazena mais dados, Cosmos DB pode mover partições lógicas para novas partições físicas, a fim de distribuir a carga entre as partições físicas.As you store more data, Cosmos DB might move logical partitions into new physical partitions, in order to spread load across the physical partitions.

Para esse teste de carga, a coleção de Cosmos DB foi provisionada com 900 RUs.For this load test, the Cosmos DB collection was provisioned with 900 RUs. O gráfico mostra 100 RU por partição física, o que implica um total de nove partições físicas.The chart shows 100 RU per physical partition, which implies a total of nine physical partitions. Embora Cosmos DB manipule automaticamente a fragmentação de partições físicas, saber que a contagem de partições pode fornecer informações sobre o desempenho.Although Cosmos DB automatically handles the sharding of physical partitions, knowing the partition count can give insight into performance. A equipe de desenvolvimento usará essas informações posteriormente, pois elas continuam a otimizar.The development team will use this information later, as they continue to optimize. Onde a linha azul cruza a linha horizontal roxa, o consumo de RU excedeu o RUs provisionado.Where the blue line crosses the purple horizontal line, RU consumption has exceeded the provisioned RUs. Esse é o ponto em que Cosmos DB começará a limitar as chamadas.That's the point where Cosmos DB will begin to throttle calls.

Teste 2: aumentar as unidades de recursosTest 2: Increase resource units

Para o segundo teste de carga, a equipe expandiu a coleção de Cosmos DB de 900 RU para 2500 RU.For the second load test, the team scaled out the Cosmos DB collection from 900 RU to 2500 RU. A taxa de transferência aumentou de 19 solicitações/segundo para 23 solicitações/segundo, e a latência média caiu de 669 MS para 569 MS.Throughput increased from 19 requests/second to 23 requests/second, and average latency dropped from 669 ms to 569 ms.

MétricaMetric Teste 1Test 1 Teste 2Test 2
Taxa de transferência (req/s)Throughput (req/sec) 1919 2323
Latência média (ms)Average latency (ms) 669669 569569
Solicitações bem-sucedidasSuccessful requests 9,8 K9.8 K 11 K11 K

Esses não são enormes ganhos, mas observar o grafo ao longo do tempo mostra uma imagem mais completa:These aren't huge gains, but looking at the graph over time shows a more complete picture:

Grafo de resultados de teste de carga do Visual Studio

Enquanto o teste anterior mostrou um pico inicial seguido por uma queda nítida, esse teste mostra uma taxa de transferência mais consistente.Whereas the previous test showed an initial spike followed by a sharp drop, this test shows more consistent throughput. No entanto, a taxa de transferência máxima não é significativamente maior.However, the maximum throughput is not significantly higher.

Todas as solicitações para Cosmos DB retornaram um status 2xx e os erros HTTP 429 foram descartados:All requests to Cosmos DB returned a 2xx status, and the HTTP 429 errors went away:

Grafo de chamadas de Cosmos DB

O grafo de consumo de RU versus RUs provisionado mostra que há bastante espaço.The graph of RU consumption versus provisioned RUs shows there is plenty of headroom. Há cerca de 275 RUs por partição física e o teste de carga com pico em cerca de 100 RUs consumidos por segundo.There are about 275 RUs per physical partition, and the load test peaked at about 100 RUs consumed per second.

Grafo de consumo de RU por partição

Outra métrica interessante é o número de chamadas para Cosmos DB por operação bem-sucedida:Another interesting metric is the number of calls to Cosmos DB per successful operation:

MétricaMetric Teste 1Test 1 Teste 2Test 2
Chamadas por operaçãoCalls per operation 1111 99

Supondo que não haja erros, o número de chamadas deve corresponder ao plano de consulta real.Assuming no errors, the number of calls should match the actual query plan. Nesse caso, a operação envolve uma consulta entre partições que atinge todas as nove partições físicas.In this case, the operation involves a cross-partition query that hits all nine physical partitions. O valor mais alto no primeiro teste de carga reflete o número de chamadas que retornaram um erro 429.The higher value in the first load test reflects the number of calls that returned a 429 error.

Essa métrica foi calculada com a execução de uma consulta de Log Analytics personalizada:This metric was calculated by running a custom Log Analytics query:

let start=datetime("2019-06-18T20:59:00.000Z");
let end=datetime("2019-07-24T21:10:00.000Z");
let operationNameToEval="GET DroneDeliveries/GetDroneUtilization";
let dependencyType="Azure DocumentDB";
let dataset=requests
| where timestamp > start and timestamp < end
| where success == true
| where name == operationNameToEval;
dataset
| project reqOk=itemCount
| summarize
    SuccessRequests=sum(reqOk),
    TotalNumberOfDepCalls=(toscalar(dependencies
    | where timestamp > start and timestamp < end
    | where type == dependencyType
    | summarize sum(itemCount)))
| project
    OperationName=operationNameToEval,
    DependencyName=dependencyType,
    SuccessRequests,
    AverageNumberOfDepCallsPerOperation=(TotalNumberOfDepCalls/SuccessRequests)

Para resumir, o segundo teste de carga mostra aperfeiçoamento.To summarize, the second load test shows improvement. No entanto, a GetDroneUtilization operação ainda leva aproximadamente uma ordem de magnitude mais longa do que a próxima operação mais lenta.However, the GetDroneUtilization operation still takes about an order of magnitude longer than the next-slowest operation. Examinar as transações de ponta a ponta ajuda a explicar o motivo:Looking at the end-to-end transactions helps to explain why:

Captura de tela de exibição de transação de ponta a ponta

Como mencionado anteriormente, a GetDroneUtilization operação envolve uma consulta entre partições para Cosmos DB.As mentioned earlier, the GetDroneUtilization operation involves a cross-partition query to Cosmos DB. Isso significa que o cliente Cosmos DB tem que desagrupar a consulta para cada partição física e coletar os resultados.This means the Cosmos DB client has to fan out the query to each physical partition and collect the results. Como mostra a exibição de transação de ponta a ponta, essas consultas são executadas em série.As the end-to-end transaction view shows, these queries are being performed in serial. A operação leva desde que a soma de todas as consultas — e esse problema seja pior apenas quando o tamanho dos dados cresce e mais partições físicas são adicionadas.The operation takes as long as the sum of all the queries — and this problem will only get worse as the size of the data grows and more physical partitions are added.

Teste 3: consultas paralelasTest 3: Parallel queries

Com base nos resultados anteriores, uma maneira óbvia de reduzir a latência é emitir as consultas em paralelo.Based on the previous results, an obvious way to reduce latency is to issue the queries in parallel. O SDK do cliente Cosmos DB tem uma configuração que controla o grau máximo de paralelismo.The Cosmos DB client SDK has a setting that controls the maximum degree of parallelism.

ValorValue DescriçãoDescription
00 Sem paralelismo (padrão)No parallelism (default)
> 0> 0 Número máximo de chamadas paralelasMaximum number of parallel calls
-1-1 O SDK do cliente seleciona um grau ideal de paralelismoThe client SDK selects an optimal degree of parallelism

Para o terceiro teste de carga, essa configuração foi alterada de 0 para-1.For the third load test, this setting was changed from 0 to -1. A tabela a seguir resume os resultados:The following table summarizes the results:

MétricaMetric Teste 1Test 1 Teste 2Test 2 Teste 3Test 3
Taxa de transferência (req/s)Throughput (req/sec) 1919 2323 4242
Latência média (ms)Average latency (ms) 669669 569569 215215
Solicitações bem-sucedidasSuccessful requests 9,8 K9.8 K 11 K11 K 20 K20 K
Solicitações limitadasThrottled requests 2,72 K2.72 K 00 00

Do grafo de teste de carga, não só a taxa de transferência geral é muito maior (a linha laranja), mas a taxa de transferência também mantém o ritmo com a carga (a linha roxa).From the load test graph, not only is the overall throughput much higher (the orange line), but throughput also keeps pace with the load (the purple line).

Grafo de resultados de teste de carga do Visual Studio

Podemos verificar se o cliente Cosmos DB está fazendo consultas em paralelo examinando a exibição de transação de ponta a ponta:We can verify that the Cosmos DB client is making queries in parallel by looking at the end-to-end transaction view:

Captura de tela de exibição de transação de ponta a ponta

Curiosamente, um efeito colateral de aumentar a taxa de transferência é que o número de RUs consumidas por segundo também aumenta.Interestingly, a side effect of increasing the throughput is that the number of RUs consumed per second also increases. Embora Cosmos DB não restringisse nenhuma solicitação durante esse teste, o consumo estava próximo do limite de RU provisionado:Although Cosmos DB did not throttle any requests during this test, the consumption was close to the provisioned RU limit:

Grafo de consumo de RU por partição

Esse grafo pode ser um sinal para expandir ainda mais o banco de dados.This graph might be a signal to further scale out the database. No entanto, acontece que podemos otimizar a consulta em vez disso.However, it turns out that we can optimize the query instead.

Etapa 4: otimizar a consultaStep 4: Optimize the query

O teste de carga anterior mostrou melhor desempenho em termos de latência e taxa de transferência.The previous load test showed better performance in terms of latency and throughput. A latência média de solicitação foi reduzida em 68% e a taxa de transferência aumentou em 220%.Average request latency was reduced by 68% and throughput increased 220%. No entanto, a consulta entre partições é uma preocupação.However, the cross-partition query is a concern.

O problema com consultas entre partições é que você paga por RU em todas as partições.The problem with cross-partition queries is that you pay for RU across every partition. Se a consulta for executada apenas ocasionalmente — , digamos, uma vez por hora, — talvez não seja relevante.If the query is only run occasionally — say, once an hour — it might not matter. Mas sempre que você vir uma carga de trabalho de leitura pesada que envolve uma consulta entre partições, deverá ver se a consulta pode ser otimizada incluindo uma chave de partição.But whenever you see a read-heavy workload that involves a cross-partition query, you should see whether the query can be optimized by including a partition key. (Talvez seja necessário recriar a coleção para usar uma chave de partição diferente.)(You might need to redesign the collection to use a different partition key.)

Aqui está a consulta para este cenário específico:Here's the query for this particular scenario:

SELECT * FROM c
WHERE c.ownerId = <ownerIdValue> and
      c.year = <yearValue> and
      c.month = <monthValue>

Essa consulta seleciona os registros que correspondem a uma ID de proprietário e mês/ano específicos.This query selects records that match a particular owner ID and month/year. No design original, nenhuma dessas propriedades é a chave de partição.In the original design, none of these properties is the partition key. Isso exige que o cliente desfolheia a consulta para cada partição física e reúna os resultados.That requires the client to fan out the query to each physical partition and gather the results. Para melhorar o desempenho da consulta, a equipe de desenvolvimento alterou o design para que a ID do proprietário seja a chave de partição da coleção.To improve query performance, the development team changed the design so that owner ID is the partition key for the collection. Dessa forma, a consulta pode ter como destino uma partição física específica.That way, the query can target a specific physical partition. (Cosmos DB trata isso automaticamente; você não precisa gerenciar o mapeamento entre os valores de chave de partição e as partições físicas.)(Cosmos DB handles this automatically; you don't have to manage the mapping between partition key values and physical partitions.)

Depois de alternar a coleção para a nova chave de partição, houve um aperfeiçoamento considerável no consumo de RU, que se traduz diretamente em custos menores.After switching the collection to the new partition key, there was a dramatic improvement in RU consumption, which translates directly into lower costs.

MétricaMetric Teste 1Test 1 Teste 2Test 2 Teste 3Test 3 Teste 4Test 4
RUs por operaçãoRUs per operation 2929 2929 2929 3.43.4
Chamadas por operaçãoCalls per operation 1111 99 1010 11

A exibição de transação de ponta a ponta mostra que, como previsto, a consulta lê apenas uma partição física:The end-to-end transaction view shows that as predicted, the query reads only one physical partition:

Captura de tela de exibição de transação de ponta a ponta

O teste de carga mostra uma taxa de transferência e latência aprimoradas:The load test shows improved throughput and latency:

MétricaMetric Teste 1Test 1 Teste 2Test 2 Teste 3Test 3 Teste 4Test 4
Taxa de transferência (req/s)Throughput (req/sec) 1919 2323 4242 5959
Latência média (ms)Average latency (ms) 669669 569569 215215 176176
Solicitações bem-sucedidasSuccessful requests 9,8 K9.8 K 11 K11 K 20 K20 K 29 K29 K
Solicitações limitadasThrottled requests 2,72 K2.72 K 00 00 00

Uma conseqüência do desempenho aprimorado é que a utilização de CPU do nó se torna muito alta:A consequence of the improved performance is that node CPU utilization becomes very high:

Grafo de utilização de nó AKS

Em direção ao final do teste de carga, a CPU média atingiu cerca de 90% e a CPU máxima atingiu 100%.Toward the end of the load test, average CPU reached about 90%, and maximum CPU reached 100%. Essa métrica indica que a CPU é o próximo afunilamento no sistema.This metric indicates that CPU is the next bottleneck in the system. Se for necessária uma taxa de transferência maior, a próxima etapa poderá estar expandindo o serviço de entrega para mais instâncias.If higher throughput is needed, the next step might be scaling out the Delivery service to more instances.

ResumoSummary

Para esse cenário, os gargalos a seguir foram identificados:For this scenario, the following bottlenecks were identified:

  • Cosmos DB solicitações de limitação devido a RUs insuficientes provisionadas.Cosmos DB throttling requests due to insufficient RUs provisioned.
  • Alta latência causada pela consulta de várias partições de banco de dados em série.High latency caused by querying multiple database partitions in serial.
  • Consulta de partição cruzada ineficiente, porque a consulta não incluiu a chave de partição.Inefficient cross-partition query, because the query did not include the partition key.

Além disso, a utilização da CPU foi identificada como um gargalo potencial em escala maior.In addition, CPU utilization was identified as a potential bottleneck at higher scale. Para diagnosticar esses problemas, a equipe de desenvolvimento examinou:To diagnose these issues, the development team looked at:

  • Latência e taxa de transferência do teste de carga.Latency and throughput from the load test.
  • Erros de Cosmos DB e consumo de RU.Cosmos DB errors and RU consumption.
  • A exibição de transação de ponta a ponta no Application insights.The end-to-end transaction view in Application Insight.
  • Utilização de CPU e memória em Azure Monitor para contêineres.CPU and memory utilization in Azure Monitor for containers.

Próximas etapasNext steps

Examinar antipadrões de desempenhoReview performance antipatterns