У нас є API, який реалізований за допомогою ServiceStack, розміщеного в IIS. Під час тестування навантажень API ми виявили, що час реакції хороший, але вони швидко погіршуються, як тільки ми вражаємо близько 3500 одночасних користувачів на сервері. У нас є два сервери, і при попаданні на них 7000 користувачів середній час відгуку сягає нижче 500 мс для всіх кінцевих точок. Поле знаходиться поза балансиром навантаження, тому ми отримуємо 3500 одночасних на сервері. Однак, як тільки ми збільшуємо кількість загальних одночасних користувачів, ми бачимо значне збільшення часу реагування. Збільшення кількості одночасних користувачів до 5000 на сервері дає нам середній час відгуку на кінцеву точку приблизно 7 секунд.
Пам'ять і процесор на серверах досить низькі, як в той час, коли час відгуку хороший, так і коли вони погіршуються. На піку з 10 000 одночасних користувачів процесор складає в середньому трохи менше 50%, а оперативна пам’ять складає близько 3-4 ГБ з 16. Це дозволяє нам думати, що ми десь досягаємо межі. На скріншоті нижче показані деякі ключові лічильники в парфмоні під час тесту навантаження із загальною кількістю 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.Чи може ми вразити обмеження щодо кількості потоків, доступних для процесу?