Чому ми отримуємо раптовий сплеск у часи реакції?


12

У нас є API, який реалізований за допомогою ServiceStack, розміщеного в IIS. Під час тестування навантажень API ми виявили, що час реакції хороший, але вони швидко погіршуються, як тільки ми вражаємо близько 3500 одночасних користувачів на сервері. У нас є два сервери, і при попаданні на них 7000 користувачів середній час відгуку сягає нижче 500 мс для всіх кінцевих точок. Поле знаходиться поза балансиром навантаження, тому ми отримуємо 3500 одночасних на сервері. Однак, як тільки ми збільшуємо кількість загальних одночасних користувачів, ми бачимо значне збільшення часу реагування. Збільшення кількості одночасних користувачів до 5000 на сервері дає нам середній час відгуку на кінцеву точку приблизно 7 секунд.

Пам'ять і процесор на серверах досить низькі, як в той час, коли час відгуку хороший, так і коли вони погіршуються. На піку з 10 000 одночасних користувачів процесор складає в середньому трохи менше 50%, а оперативна пам’ять складає близько 3-4 ГБ з 16. Це дозволяє нам думати, що ми десь досягаємо межі. На скріншоті нижче показані деякі ключові лічильники в парфмоні під час тесту навантаження із загальною кількістю 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 запитів = 3 500+ журналу IIS. Якщо вони знаходяться на одному диску / розділі, у вас може виникнути велика проблема.
Течі Джо

Відповіді:


2

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

Я пропоную:

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

  2. Використовуйте нитки пулів, якщо їх не використовуєте.

  3. Щодо зовнішніх дзвінків ви сказали: "Ми також реалізували послуги з отримання асинхронних даних із зовнішніх джерел, щоб кінцева точка повинна бути такою ж повільною, як і найповільніший зовнішній дзвінок (якщо, звичайно, у нас немає даних у кеші). "

Питання: - Ви перевіряли, чи заблоковані якісь дані кешу під час зовнішнього дзвінка або лише під час запису зовнішнього виклику в кеш? (занадто очевидно, але треба сказати). - Ви заблокуєте весь кеш або невеликі його частини? (занадто очевидно, але треба сказати). - Навіть якщо вони асинхронні, як часто виконуються зовнішні дзвінки? Навіть якщо вони не запускаються так часто, вони можуть бути заблоковані надмірною кількістю запитів до кешу від дзвінків користувача, поки кеш заблокований. Цей сценарій, як правило, показує фіксований відсоток використовуваного процесора, оскільки багато потоків чекають з фіксованими інтервалами, і "блокуванням" також потрібно керувати. - Ви перевіряли, чи означає, що зовнішні завдання означають, що час відповіді також збільшується при надходженні повільного сценарію?

Якщо проблема все ще зберігається, я б рекомендував уникати класу Task та здійснювати зовнішні дзвінки через той самий пул потоків, який керує запитами користувачів. Це уникнути попереднього сценарію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.