Почему мы получаем внезапный всплеск времени отклика?

12

У нас есть API, который реализован с использованием ServiceStack, который размещен в IIS. Выполняя нагрузочное тестирование API, мы обнаружили, что время отклика хорошее, но оно быстро ухудшается, как только мы получаем около 3500 одновременных пользователей на сервер. У нас есть два сервера, и при их использовании 7000 пользователей среднее время отклика для всех конечных точек составляет менее 500 мс. Ящики находятся за балансировщиком нагрузки, поэтому мы получаем 3500 параллелей на сервер. Однако, как только мы увеличиваем количество одновременных пользователей, мы видим значительное увеличение времени отклика. Увеличение числа одновременных пользователей до 5000 на сервер дает нам среднее время ответа на конечную точку около 7 секунд.

Объем памяти и ЦП на серверах довольно низок, как при хороших временах отклика, так и после их ухудшения. На пике с 10 000 одновременных пользователей средняя загрузка ЦП чуть ниже 50%, а объем оперативной памяти составляет около 3-4 ГБ из 16. Это заставляет нас думать, что мы где-то достигли какого-то предела. На приведенном ниже снимке экрана показаны некоторые ключевые счетчики в perfmon во время нагрузочного теста с общим числом пользователей в 10 000 одновременно. Подсвеченный счетчик запросов / сек. Справа от скриншота видно, что график запросов в секунду становится действительно ошибочным. Это основной индикатор для медленного времени отклика. Как только мы видим этот паттерн, мы замечаем медленное время отклика в нагрузочном тесте.

скриншот perfmon с подсвеченными запросами в секунду

Как нам решить проблему с производительностью? Мы пытаемся определить, является ли это проблемой кодирования или проблемой конфигурации. Есть ли какие-либо настройки в web.config или IIS, которые могли бы объяснить это поведение? Пул приложений работает под управлением .NET v4.0, а версия IIS - 7.5. Единственное изменение, которое мы внесли в настройки по умолчанию, - это обновить значение длины очереди пула приложений с 1000 до 5000. Мы также добавили следующие параметры конфигурации в файл Aspnet.config:

<system.web>
    <applicationPool 
        maxConcurrentRequestsPerCPU="5000"
        maxConcurrentThreadsPerCPU="0" 
        requestQueueLimit="5000" />
</system.web>

Больше деталей:

Целью API является объединение данных из различных внешних источников и возвращение в виде JSON. В настоящее время он использует реализацию кэша InMemory для кэширования отдельных внешних вызовов на уровне данных. Первый запрос к ресурсу извлечет все необходимые данные, а любые последующие запросы к тому же ресурсу получат результаты из кэша. У нас есть «обработчик кэша», который реализован как фоновый процесс, который обновляет информацию в кэше через определенные заданные интервалы. Мы добавили блокировку вокруг кода, который извлекает данные из внешних ресурсов. Мы также реализовали сервисы для извлечения данных из внешних источников асинхронным образом, чтобы конечная точка работала так же медленно, как и самый медленный внешний вызов (если, конечно, у нас нет данных в кеше). Это делается с помощью класса System.Threading.Tasks.Task.Можем ли мы ограничить количество потоков, доступных для процесса?

Кристиан Хагелид
источник
5
Сколько ядер у вашего процессора? Возможно, вы используете одно ядро. Когда магическое число составляет 50%, 25% или 12,5%, это говорит о том, что вы исчерпали ядро ​​и по какой-то причине не можете использовать другие ядра, которые бездействуют. Проверьте на максимум сердечник.
Дэвид Шварц
1
У вас есть одна тема на запрос? Итак, на 5000 запросов у вас есть 5000 потоков? Если вы это сделаете, то это, вероятно, ваша проблема. Вместо этого вы должны создать пул потоков и использовать пул потоков для обработки запросов, помещая их в очередь по мере их поступления в пул потоков. Когда поток завершил работу с запросом, он может обработать запрос из очереди. Такого рода обсуждения лучше всего подходят для работы со стеком. Слишком много потоков означает слишком много переключений контекста.
Мэтт
1
Здесь просто проверка работоспособности. Вы пытались отключить все свои фоновые процессы и посмотреть, каково поведение JSON, возвращающего статические данные из кэша? Другими словами, создание вашего JSON запрашивает статические данные и удаляет «внешние асинхронные вызовы», которые полностью обновляют ваш кэш. Кроме того, в зависимости от количества данных JSON, обслуживаемых по каждому запросу, задумывались ли вы о пропускной способности вашей сети и о том, начинают ли резервные копии запросы, потому что серверы просто не могут вытолкнуть данные достаточно быстро?
Роберт
1
+1 к предложению Давида выше. Вы должны действительно повторить тест и внимательно посмотреть на каждое использование ядра. Я бы посоветовал вам сделать это как можно скорее, чтобы устранить это, если ничего больше. Во-вторых, я немного подозрительно отношусь к вашему кешу. Конфликт блокировок может показать именно такое поведение - в некоторых критических точках блокировки вызывают задержки, которые, в свою очередь, приводят к тому, что блокировки удерживаются дольше, чем обычно, вызывая переломный момент, когда дела идут быстро вниз. Можете ли вы поделиться своим кодом кэширования и блокировки?
Стив Кук
1
Каковы настройки диска для серверов (при условии, что, поскольку они сбалансированы по нагрузке, настройка диска одинакова)? Можете ли вы опубликовать все спецификации для дисков / серверов в вашем первоначальном посте? Вы бросили perfmon на диск (и) на физическом диске (дисках), на котором существуют IIS И файлы журнала IIS? Вполне возможно, что у вас проблемы с диском из-за того, что 3500 запросов = 3500+ записей журнала IIS. Если они находятся на одном диске / разделе, у вас могут возникнуть большие проблемы.
Технарь Джо

Ответы:

2

Вслед за @DavidSchwartz и @Matt это похоже на потоки, блокирует проблему управления.

Я предлагаю:

  1. Заморозьте внешние вызовы и сгенерированный для них кеш и запустите нагрузочный тест со статической внешней информацией, чтобы просто устранить любую проблему, не связанную со стороной серверной среды.

  2. Используйте пулы потоков, если не используете их.

  3. О внешних вызовах вы сказали: «Мы также внедрили службы для извлечения данных из внешних источников асинхронным способом, чтобы конечная точка работала так же медленно, как и самый медленный внешний вызов (если, конечно, у нас нет данных в кэше). "

Вопросы: - Вы проверили, заблокированы ли какие-либо данные кэша во время внешнего вызова или только при записи результата внешнего вызова в кэш? (слишком очевидно, но должен сказать). - Вы блокируете весь кеш или его мелкие части? (слишком очевидно, но должен сказать). - Даже если они асинхронные, как часто выполняются внешние вызовы? Даже если они запускаются не так часто, они могут быть заблокированы из-за чрезмерного количества запросов к кешу от пользовательских вызовов, пока кеш заблокирован. Этот сценарий обычно показывает фиксированный процент использования ЦП, поскольку многие потоки ожидают через фиксированные интервалы, и «блокировка» также должна управляться. - Проверяли ли вы, что внешние задачи означают, что время отклика также увеличивается при появлении медленного сценария?

Если проблема не устранена, я бы рекомендовал избегать использования класса Task и выполнять внешние вызовы через тот же пул потоков, который управляет пользовательскими запросами. Это чтобы избежать предыдущего сценария.

SaintJob 2.0
источник