Назад | Перейти на главную страницу

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

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

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

Как решить эту проблему с производительностью? Мы пытаемся определить, является ли это проблемой кодирования или проблемой конфигурации. Есть ли какие-либо настройки в 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. Можем ли мы столкнуться с ограничением количества потоков, доступных для процесса?

После @DavidSchwartz и @Matt это выглядит как потоки, блокирует проблему управления.

Я предлагаю:

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

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

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

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

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