Сценарий настройки производительности: несколько серверных службPerformance tuning scenario: Multiple backend services

В этой статье описывается, как группа разработчиков использовала метрики для поиска узких мест и повышения производительности распределенной системы.This article describes how a development team used metrics to find bottlenecks and improve the performance of a distributed system. Статья основана на фактическом нагрузочном тестировании, которое мы сделали для примера приложения.The article is based on actual load testing that we did for a sample application. Код приложения доступен на сайте GitHubвместе с проектом нагрузочного теста Visual Studio, который используется для создания результатов.The application code is available on GitHub, along with the Visual Studio load test project used to generate the results.

Эта статья входит в серию. Ознакомьтесь с первой частью этойобласти.This article is part of a series. Read the first part here.

Сценарий. вызов нескольких серверных служб для получения сведений и последующей статистической обработки результатов.Scenario: Call multiple backend services to retrieve information and then aggregate the results.

В этом сценарии используется приложение доставки помощью дронов.This scenario involves a drone delivery application. Клиенты могут запрашивать REST API для получения последних сведений о счете.Clients can query a REST API to get their latest invoice information. Счет включает сводку по доставке клиента, пакетам и общему использованию помощью дронов.The invoice includes a summary of the customer's deliveries, packages, and total drone utilization. Это приложение использует архитектуру микрослужб, работающую в службе Azure Kubernetes Service (AKS), и сведения, необходимые для выставления счета, распределяются по нескольким микрослужбам.This application uses a microservices architecture running on Azure Kubernetes Service (AKS), and the information needed for the invoice is spread across several microservices.

Вместо того, чтобы клиент вызывал каждую службу напрямую, приложение реализует шаблон объединения шлюза .Rather than the client calling each service directly, the application implements the Gateway Aggregation pattern. Используя этот шаблон, клиент выполняет один запрос к службе шлюза.Using this pattern, the client makes a single request to a gateway service. Шлюз, в свою очередь, обращается к внутренним службам параллельно, а затем объединяет результаты в отдельные полезные данные ответа.The gateway in turn calls the backend services in parallel, and then aggregates the results into a single response payload.

Схема, демонстрирующая схему агрегирования шлюза

Тест 1: производительность базовых показателейTest 1: Baseline performance

Чтобы создать базовый план, группа разработчиков начала выполнять нагрузочный тест, изменяя нагрузку от одного смоделированного пользователя до 40 пользователей с общей длительностью в 8 минут.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. На следующей диаграмме, взятой из Visual Studio, показаны результаты.The following chart, taken from Visual Studio, shows the results. Фиолетовая линия показывает пользовательскую нагрузку, а оранжевая — пропускная способность (среднее число запросов в секунду).The purple line shows the user load, and the orange line shows throughput (average requests per second).

Диаграмма результатов нагрузочного теста Visual Studio

Красная линия в нижней части диаграммы показывает, что клиенту не было возвращено никаких ошибок, что является поощрением.The red line along the bottom of the chart shows that no errors were returned to the client, which is encouraging. Тем не менее средняя пропускная способность в среднем проходит через тестирование, а затем отпадает на остаток, даже пока нагрузка увеличивается.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. Это указывает на то, что серверная части не может поддерживаться.That indicates the back end is not able to keep up. Приведенный здесь шаблон является типичным, когда система начинает обращаться к ограничениям ресурсов по — истечении максимальной пропускной способности.The pattern seen here is common when a system starts to reach resource limits — after reaching a maximum, throughput actually falls significantly. Конфликты ресурсов, временные ошибки или увеличение скорости исключений могут повлиять на этот шаблон.Resource contention, transient errors, or an increase in the rate of exceptions can all contribute to this pattern.

Давайте подробно рассмотрим данные мониторинга, чтобы узнать, что происходит внутри системы.Let's dig into the monitoring data to learn what's happening inside the system. Следующая диаграмма берется из Application Insights.The next chart is taken from Application Insights. Показывает среднюю длительность вызовов HTTP из шлюза к серверным службам.It shows the average durations of the HTTP calls from the gateway to the backend services.

График длительности вызовов HTTP

На этой диаграмме показано, что одна операция, в частности, GetDroneUtilization занимает гораздо больше времени в среднем — по порядку.This chart shows that one operation in particular, GetDroneUtilization, takes much longer on average — by an order of magnitude. Шлюз выполняет эти вызовы параллельно, поэтому самая медленная операция определяет время, затрачиваемое на завершение всего запроса.The gateway makes these calls in parallel, so the slowest operation determines how long it takes for the entire request to complete.

Очевидно, что следующий шаг — подробное изучение GetDroneUtilization операции и поиск узких мест.Clearly the next step is dig into the GetDroneUtilization operation and look for any bottlenecks. Одна из возможных ситуации — нехватка ресурсов.One possibility is resource exhaustion. Возможно, в данной внутренней службе недостаточно ресурсов ЦП или памяти.Perhaps this particular backend service is running out of CPU or memory. Для кластера AKS эта информация доступна в портал Azure с помощью функции Azure Monitor для контейнеров .For an AKS cluster, this information is available in the Azure portal through the Azure Monitor for containers feature. На следующих диаграммах показано использование ресурсов на уровне кластера.The following graphs show resource utilization at the cluster level:

Диаграмма использования узла AKS

На этом снимке экрана показаны как средние, так и максимальные значения.In this screenshot, both the average and maximum values are shown. Важно взглянуть не только на среднее, поскольку среднее может скрыть пики в данных.It's important to look at more than just the average, because the average can hide spikes in the data. Здесь средняя загрузка ЦП остается ниже 50%, но в 80% существует несколько пиковых нагрузок.Here, the average CPU utilization stays below 50%, but there are a couple of spikes to 80%. Это близко к емкости, но по-прежнему в пределах допустимых пределов.That's close to capacity but still within tolerances. Что-то еще вызывает узкое место.Something else is causing the bottleneck.

Следующая диаграмма показывает истинный виновник.The next chart reveals the true culprit. На этой диаграмме показаны коды ответов HTTP из серверной базы данных службы доставки, в данном случае это Cosmos DB.This chart shows HTTP response codes from the Delivery service's backend database, which in this case is Cosmos DB. Синяя линия представляет коды успеха (HTTP 2xx), а зеленая — ошибки HTTP 429.The blue line represents success codes (HTTP 2xx), while the green line represents HTTP 429 errors. Код возврата HTTP 429 означает, что Cosmos DB выполняет временное регулирование запросов, так как вызывающий объект потребляет больше единиц ресурсов (RU), чем было подготовлено.An HTTP 429 return code means that Cosmos DB is temporarily throttling requests, because the caller is consuming more resource units (RU) than provisioned.

График регулируемых запросов

Чтобы получить дополнительные сведения, Группа разработки использовала Application Insights для просмотра комплексной телеметрической информации для репрезентативной выборки запросов.To get further insight, the development team used Application Insights to view the end-to-end telemetry for a representative sample of requests. Вот один экземпляр:Here is one instance:

Снимок экрана: сквозное представление транзакций

В этом представлении отображаются вызовы, связанные с одним запросом клиента, а также сведения о времени и коды ответов.This view shows the calls related to a single client request, along with timing information and response codes. Вызовы верхнего уровня передаются из шлюза в серверные службы.The top-level calls are from the gateway to the backend services. Вызов метода GetDroneUtilization разворачивается для отображения вызовов внешних зависимостей — в данном случае для Cosmos DB.The call to GetDroneUtilization is expanded to show calls to external dependencies — in this case, to Cosmos DB. Вызов в Red вернул ошибку HTTP 429.The call in red returned an HTTP 429 error.

Обратите внимание на большой зазор между ошибкой HTTP 429 и следующим вызовом.Note the large gap between the HTTP 429 error and the next call. Когда клиентская библиотека Cosmos DB получает ошибку HTTP 429, она автоматически отключается и ожидает повторного выполнения операции.When the Cosmos DB client library receives an HTTP 429 error, it automatically backs off and waits to retry the operation. В этом представлении в течение 672 МС эта операция заняла, и большая часть времени была потрачена на ожидание повторного 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.

Вот еще один интересный граф для этого анализа.Here's another interesting graph for this analysis. В нем показывается потребление единиц запросов на один физический раздел по сравнению с подготовленным числом допусков в секунду:It shows RU consumption per physical partition versus provisioned RUs per physical partition:

График потребления единиц запросов на секцию

Чтобы иметь представление о графике, необходимо понять, как Cosmos DB управляет секциями.To make sense of this graph, you need to understand how Cosmos DB manages partitions. Коллекции в Cosmos DB могут иметь ключ секции.Collections in Cosmos DB can have a partition key. Каждое возможное значение ключа определяет логическую секцию данных в коллекции.Each possible key value defines a logical partition of the data within the collection. Cosmos DB распределяет эти логические секции по одной или нескольким физическим секциям.Cosmos DB distributes these logical partitions across one or more physical partitions. Управление физическими секциями осуществляется автоматически с Cosmos DB.The management of physical partitions is handled automatically by Cosmos DB. При хранении дополнительных данных Cosmos DB может переместить логические секции в новые физические секции, чтобы распределить нагрузку между физическими секциями.As you store more data, Cosmos DB might move logical partitions into new physical partitions, in order to spread load across the physical partitions.

Для этого нагрузочного теста коллекция Cosmos DB была подготовлена с 900 RUs.For this load test, the Cosmos DB collection was provisioned with 900 RUs. На диаграмме показано 100 единиц запросов на физическую секцию, что подразумевает всего девять физических секций.The chart shows 100 RU per physical partition, which implies a total of nine physical partitions. Хотя Cosmos DB автоматически обрабатывает сегментирование физических секций, знание счетчика секций позволяет получить представление о производительности.Although Cosmos DB automatically handles the sharding of physical partitions, knowing the partition count can give insight into performance. Команда разработчиков будет использовать эти сведения позже, так как они продолжат оптимизацию.The development team will use this information later, as they continue to optimize. Когда синяя линия пересекает фиолетовой горизонтальной линией, потребление единиц запросов превысило подготовленный объект RUs.Where the blue line crosses the purple horizontal line, RU consumption has exceeded the provisioned RUs. Это точка, где Cosmos DB начнет регулировать вызовы.That's the point where Cosmos DB will begin to throttle calls.

Тест 2. увеличение единиц ресурсовTest 2: Increase resource units

Для второго нагрузочного теста команда масштабирует коллекцию Cosmos DB от 900 до 2500 единиц запросов.For the second load test, the team scaled out the Cosmos DB collection from 900 RU to 2500 RU. Пропускная способность увеличилась с 19 запросов в секунду до 23 запросов в секунду, а средняя задержка отброшена от 669 МС до 569 МС.Throughput increased from 19 requests/second to 23 requests/second, and average latency dropped from 669 ms to 569 ms.

МетрикаMetric Тест 1Test 1 Тест 2Test 2
Пропускная способность (запросов/с)Throughput (req/sec) 1919 2323
Средняя задержка (мс)Average latency (ms) 669669 569569
Успешные запросы.Successful requests 9,8 л9.8 K 11 КБ11 K

Это не огромная прибыль, но при просмотре графика с течением времени отображается более полная картина:These aren't huge gains, but looking at the graph over time shows a more complete picture:

Диаграмма результатов нагрузочного теста Visual Studio

В то время как предыдущий тест показал первоначальный пик, за которым следует четкий сброс, этот тест показывает более последовательную пропускную способность.Whereas the previous test showed an initial spike followed by a sharp drop, this test shows more consistent throughput. Однако максимальная пропускная способность не значительно выше.However, the maximum throughput is not significantly higher.

Все запросы к Cosmos DB возвращали состояние 2xx, а ошибки HTTP 429 исчезают:All requests to Cosmos DB returned a 2xx status, and the HTTP 429 errors went away:

Граф вызовов Cosmos DB

На графе потребления единиц запросов и подготовленных данных в версии-в выпуске RUs есть много ресурсов.The graph of RU consumption versus provisioned RUs shows there is plenty of headroom. Около 275 кбит/с на физический раздел, а нагрузочный тест находится в скором объеме примерно 100 в секунду.There are about 275 RUs per physical partition, and the load test peaked at about 100 RUs consumed per second.

График потребления единиц запросов на секцию

Еще одна интересная метрика — число вызовов Cosmos DB за успешную операцию:Another interesting metric is the number of calls to Cosmos DB per successful operation:

МетрикаMetric Тест 1Test 1 Тест 2Test 2
Вызовов на операциюCalls per operation 1111 99

При отсутствии ошибок количество вызовов должно соответствовать фактическому плану запроса.Assuming no errors, the number of calls should match the actual query plan. В этом случае операция включает запрос между секциями, который обращается ко всем девяти физическим секциям.In this case, the operation involves a cross-partition query that hits all nine physical partitions. Большее значение в первом нагрузочном тесте отражает количество вызовов, которые возвращали ошибку 429.The higher value in the first load test reflects the number of calls that returned a 429 error.

Эта метрика вычисляется путем запуска пользовательского запроса Log Analytics: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)

Для суммирования второй нагрузочный тест показывает улучшение.To summarize, the second load test shows improvement. Однако GetDroneUtilization операция по-прежнему выполняется в порядке, отличном от следующей самой медленной операции.However, the GetDroneUtilization operation still takes about an order of magnitude longer than the next-slowest operation. Рассмотрение комплексных транзакций помогает объяснить, почему:Looking at the end-to-end transactions helps to explain why:

Снимок экрана: сквозное представление транзакций

Как упоминалось ранее, GetDroneUtilization операция включает запрос между секциями для Cosmos DB.As mentioned earlier, the GetDroneUtilization operation involves a cross-partition query to Cosmos DB. Это означает, что Cosmos DB клиент должен выполнить запрос к каждому физическому разделу и получить результаты.This means the Cosmos DB client has to fan out the query to each physical partition and collect the results. Как видно из представления сквозной транзакции, эти запросы выполняются последовательно.As the end-to-end transaction view shows, these queries are being performed in serial. Операция выполняется до тех пор, пока сумма всех запросов — и эта проблема будут хуже, так как размер данных растет и добавляются дополнительные физические секции.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.

Тест 3. параллельные запросыTest 3: Parallel queries

На основе предыдущих результатов очевидным способом сокращения задержки является параллельное выдачу запросов.Based on the previous results, an obvious way to reduce latency is to issue the queries in parallel. Пакет SDK для клиента Cosmos DB имеет параметр, управляющий максимальной степенью параллелизма.The Cosmos DB client SDK has a setting that controls the maximum degree of parallelism.

ЗначениеValue ОписаниеDescription
00 Без параллелизма (по умолчанию)No parallelism (default)
> 0> 0 Максимальное число параллельных вызововMaximum number of parallel calls
-1-1 Клиентский пакет SDK выбирает оптимальную степень параллелизмаThe client SDK selects an optimal degree of parallelism

Для третьего нагрузочного теста этот параметр изменен с 0 на-1.For the third load test, this setting was changed from 0 to -1. В следующей таблице приведена сводка по результатам.The following table summarizes the results:

МетрикаMetric Тест 1Test 1 Тест 2Test 2 Тест 3Test 3
Пропускная способность (запросов/с)Throughput (req/sec) 1919 2323 4242
Средняя задержка (мс)Average latency (ms) 669669 569569 215215
Успешные запросы.Successful requests 9,8 л9.8 K 11 КБ11 K 20 КБ20 K
Регулируемые запросыThrottled requests 2,72 л2.72 K 00 00

На диаграмме нагрузочного теста не только общая пропускная способность (оранжевая линия), но пропускная способность также обеспечивает скорость загрузки (фиолетовые линии).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).

Диаграмма результатов нагрузочного теста Visual Studio

Можно проверить, что клиент Cosmos DB параллельно опрашивает запросы, просмотрев представление сквозной транзакции:We can verify that the Cosmos DB client is making queries in parallel by looking at the end-to-end transaction view:

Снимок экрана: сквозное представление транзакций

Любопытно, что при увеличении пропускной способности побочный результат также увеличивается.Interestingly, a side effect of increasing the throughput is that the number of RUs consumed per second also increases. Хотя Cosmos DB не регулирования каких-либо запросов во время этого теста, потребление было близко к подготовленному ограничению в ЕДИНИЦах:Although Cosmos DB did not throttle any requests during this test, the consumption was close to the provisioned RU limit:

График потребления единиц запросов на секцию

Этот граф может быть сигналом для дальнейшего масштабирования базы данных.This graph might be a signal to further scale out the database. Тем не менее оказывается, что мы можем оптимизировать запрос.However, it turns out that we can optimize the query instead.

Шаг 4. Оптимизация запросаStep 4: Optimize the query

Предыдущий нагрузочный тест показал лучшую производительность с точки зрения задержки и пропускной способности.The previous load test showed better performance in terms of latency and throughput. Средняя задержка запросов снизилась на 68%, а пропускная способность увеличилась 220%.Average request latency was reduced by 68% and throughput increased 220%. Однако запрос между секциями является важным фактором.However, the cross-partition query is a concern.

Проблема запросов между секциями заключается в том, что каждый раздел оплачивается по ЕДИНИЦам.The problem with cross-partition queries is that you pay for RU across every partition. Если запрос выполняется только иногда — , скажем, один час — может не иметь значения.If the query is only run occasionally — say, once an hour — it might not matter. Но когда вы видите рабочую нагрузку с интенсивным чтением, в которой задействованы запросы между секциями, вы должны определить, можно ли оптимизировать запрос, включив ключ секции.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. (Может потребоваться изменить структуру коллекции для использования другого ключа секции.)(You might need to redesign the collection to use a different partition key.)

Вот запрос для этого конкретного сценария:Here's the query for this particular scenario:

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

Этот запрос выбирает записи, соответствующие определенному ИДЕНТИФИКАТОРу владельца и месяцу/году.This query selects records that match a particular owner ID and month/year. В исходном проекте ни одно из этих свойств не является ключом секции.In the original design, none of these properties is the partition key. Для этого клиент должен выполнить запрос к каждому физическому разделу и собрать результаты.That requires the client to fan out the query to each physical partition and gather the results. Чтобы повысить производительность запросов, группа разработчиков изменила структуру, чтобы идентификатор владельца был ключом секции для коллекции.To improve query performance, the development team changed the design so that owner ID is the partition key for the collection. Таким образом, запрос может ориентироваться на определенную физическую секцию.That way, the query can target a specific physical partition. (Cosmos DB обрабатывает это автоматически; вам не нужно управлять сопоставлением между значениями ключей секций и физическими секциями.)(Cosmos DB handles this automatically; you don't have to manage the mapping between partition key values and physical partitions.)

После переключения коллекции на новый ключ секции было значительно улучшено потребление единиц запросов, которое преобразуется в более низкие затраты.After switching the collection to the new partition key, there was a dramatic improvement in RU consumption, which translates directly into lower costs.

МетрикаMetric Тест 1Test 1 Тест 2Test 2 Тест 3Test 3 Тест 4Test 4
Число получателей на операциюRUs per operation 2929 2929 2929 3.43.4
Вызовов на операциюCalls per operation 1111 99 1010 11

Представление сквозной транзакции показывает, что прогнозируемый запрос считывает только одну физическую секцию:The end-to-end transaction view shows that as predicted, the query reads only one physical partition:

Снимок экрана: сквозное представление транзакций

В нагрузочном тесте показана Улучшенная пропускная способность и задержка:The load test shows improved throughput and latency:

МетрикаMetric Тест 1Test 1 Тест 2Test 2 Тест 3Test 3 Тест 4Test 4
Пропускная способность (запросов/с)Throughput (req/sec) 1919 2323 4242 5959
Средняя задержка (мс)Average latency (ms) 669669 569569 215215 176176
Успешные запросы.Successful requests 9,8 л9.8 K 11 КБ11 K 20 КБ20 K 29 л29 K
Регулируемые запросыThrottled requests 2,72 л2.72 K 00 00 00

Следствием повышения производительности является то, что использование ЦП узла становится очень высоким:A consequence of the improved performance is that node CPU utilization becomes very high:

Диаграмма использования узла AKS

В конце нагрузочного теста достигнуто среднее время ЦП, равное 90%, а максимальное количество ПРОЦЕССОРов достигло 100%.Toward the end of the load test, average CPU reached about 90%, and maximum CPU reached 100%. Эта метрика указывает на то, что ЦП является следующим узким местом в системе.This metric indicates that CPU is the next bottleneck in the system. Если требуется более высокая пропускная способность, следующий шаг может привести к масштабированию службы доставки до большего числа экземпляров.If higher throughput is needed, the next step might be scaling out the Delivery service to more instances.

СводкаSummary

В этом сценарии обнаружены следующие узкие места:For this scenario, the following bottlenecks were identified:

  • Cosmos DB запросов регулирования из-за недостаточной подготовки "RUs".Cosmos DB throttling requests due to insufficient RUs provisioned.
  • Высокая задержка, вызванная запросом нескольких секций базы данных в последовательном запросе.High latency caused by querying multiple database partitions in serial.
  • Неэффективный запрос между секциями, так как запрос не включал ключ секции.Inefficient cross-partition query, because the query did not include the partition key.

Кроме того, использование ЦП было идентифицировано как потенциальное узкое место в более высоком масштабе.In addition, CPU utilization was identified as a potential bottleneck at higher scale. Для диагностики этих проблем команда разработчиков выглядела следующим образом:To diagnose these issues, the development team looked at:

  • Задержка и пропускная способность нагрузочного теста.Latency and throughput from the load test.
  • Cosmos DB ошибок и потребления единиц запросов.Cosmos DB errors and RU consumption.
  • Представление сквозной транзакции в Application Insights.The end-to-end transaction view in Application Insight.
  • Использование ЦП и памяти в Azure Monitor для контейнеров.CPU and memory utilization in Azure Monitor for containers.

Дальнейшие действияNext steps

Обзор антишаблонов производительностиReview performance antipatterns