Далі ви розгортаєте додаток, вводите його в експлуатацію і воно працює чудово протягом декількох перших тижнів, але зі збільшенням кількості користувачів збільшується і кількість запитів, які потрібно обробити вашого сервера, і раптом, ваш сервер починає захлинатися. Спочатку це може проявлятися в збільшенні часу обробки запитів, потім робочий процес починає використовувати все більше пам'яті і обчислювальних ресурсів і в кінцевому підсумку веб сервер просто перестає встигати обробляти всі запити і в файлах журналів починають все частіше з'являтися повідомлення HTTP 500 ( «Internal Server Error» ).
Що трапилося? Може бути знову зайнятися оптимізацією програми? Однак, зі збільшенням кількості користувачів ситуація повториться. Може бути збільшити обсяг пам'яті або додати процесори? Однак подібне розширення можливостей єдиного комп'ютера має свої межі. Прийшов час визнати той факт, що вам необхідні додаткові сервери.
Горизонтальне масштабування (scaling out) веб-додатків - це природний процес, що починається в певний момент в житті веб-додатків. Один сервер може одночасно обслуговувати десятки, сотні і навіть тисячі користувачів, але він не в змозі досить довго витримувати пікові навантаження. Пам'ять починає заповнюватися інформацією про сеансах, обробка нових запитів призупиняється через відсутність вільних потоків виконання, і перемикання контексту починають виконуватися занадто часто, що веде до збільшення затримок і зниження пропускної здатності сервера.
горизонтальне масштабування
З архітектурної точки зору, виконати масштабування зовсім не складно: достатньо придбати ще один-два комп'ютери (або десять), розмістити сервери за комп'ютером, який виконує розподіл навантаження, і все! Але проблема в тому, що зазвичай все не так просто.
Однією з основних проблем горизонтального масштабування, з якими стикаються розробники - як реалізувати прив'язку до сервера. Наприклад, коли працює єдиний веб-сервер, інформація про стан сеансів користувачів зберігається в пам'яті. Якщо додати ще один сервер, як забезпечити для нього доступ до об'єктів сеансів? Як синхронізувати сеанси між серверами?
Деякі веб-розробники вирішують цю проблему, зберігаючи інформацію на сервері і пов'язуючи клієнта з конкретним сервером. Як тільки клієнт з'єднається з одним з серверів, що знаходяться за балансувальник навантаження, з цього момент всі запити від цього клієнта будуть направлятися одному і тому ж веб-сервера. Цей прийом називається також прив'язкою сеансу. Прив'язка сеансу - це обхідний рішення, але воно не вирішує проблему, тому що не дозволяє рівномірно розподіляти навантаження між серверами. Використовуючи цей прийом, легко потрапити в ситуацію, коли один сервер буде обслуговувати досить багато користувачів, а інші будуть в цей час простоювати, тому що їхні клієнти вже закінчили роботу і відключилися.
Тому це рішення полягає в тому, щоб не використовувати пам'ять комп'ютера для зберігання таких дані, як інформація про сеанси користувачів або кеш. Але як зберігання кеша в пам'яті певного комп'ютера може перешкодити масштабування?
Уявіть, що станеться, коли користувач пошле запит, що викликає оновлення кеша: сервер, який отримав запит, оновить свій кеш в пам'яті, але інші сервери не будуть знати, що це необхідно зробити, і якщо в їх кешах зберігається копія того ж об'єкта, це призведе до суперечливості даних в масштабі всього програми. Один із способів вирішення цієї проблеми - організувати синхронізацію об'єктів в кеші між серверами. Таке цілком можливо, але це ускладнить загальну архітектуру веб-додатки, не кажучи вже про те, як виросте обсяг трафіку між серверами.
Механізми масштабування в ASP.NET
Горизонтальне масштабування вимагає зберігання інформації про стан за межами процесів. У ASP.NET є два механізми, що забезпечують такий спосіб зберігання даних:
Служба управління станом (State Service)
Служба управління станом - це служба Windows, що підтримує управління станом для декількох комп'ютерів. Ця служба встановлюється автоматично при установці .NET Framework, але вона виключена за замовчуванням. Вам достатньо просто вибрати, на якому сервері буде виконуватися служба управління станом, і налаштувати всі інші на її використання. Незважаючи на те, що служба управління станом дозволяє декільком серверам використовувати загальне сховище інформації, вона не підтримує можливість довготривалого зберігання. Тобто, якщо щось трапиться з сервером, де виконується ця служба, вся інформація про сеанси в вашій веб-фермі буде загублена.
ASP.NET підтримує можливість зберігання інформації про стан в базі даних SQL Server. Цей механізм не тільки підтримує ті ж можливості, що і служба управління станом, але також забезпечує довгострокове зберігання даних, тому, навіть якщо на веб-серверах і на сервері з базою даних SQL Server трапиться аварійна ситуація, інформація про стан збережеться.
Для потреб кешування в більшості випадків можна з успіхом використовувати один з механізмів розподіленого кешування, таких як Microsoft AppFabric Cache. NCache або Memcached. останній з яких є відкритою реалізацією розподіленого кеша.
Механізм розподіленого кешування дозволяє об'єднати пам'ять кількох серверів в один розподілений кеш. Розподілені кеши підтримують абстракцію місцезнаходження, і від вас не буде потрібно знати, де знаходиться кожен фрагмент даних, служби повідомлень допоможуть залишатися в курсі - де і що змінилося, а висока доступність гарантує, що навіть в разі аварії на одному з серверів дані не будуть загублені.
Деякі розподілені кеши, такі як AppFabric Cache і Memcached, також мають власні реалізації служби управління станом і провайдерів кеша для ASP.NET.
Пастки горизонтального масштабування
Хоча це і не має прямого відношення до продуктивності, все ж варто позначити деякі проблеми, з якими можна зіткнутися при масштабуванні веб-додатків.
Деякі частини веб-додатків вимагають використання особливих ключів безпеки для генерації унікальних ідентифікаторів, щоб запобігти можливості обману веб-додатки і вторгнення в нього. Наприклад, унікальний ключ використовується в процедурі аутентифікації FormsAuthentication і при шифруванні даних механізмом збереження стану уявлення. За замовчуванням ключі безпеки для веб-додатків генеруються кожен раз, коли запускається пул додатка.
У випадку з єдиним сервером це не викликає ніяких проблем, але коли веб-додаток виконується на декількох серверах, це може перетворитися в проблему, так як кожен сервер буде мати свій власний унікальний ключ. Уявіть таку ситуацію: клієнт надсилає запит серверу A і отримує у відповідь cookie, підписаний унікальним ключем сервера A, потім клієнт посилає новий запит з прийнятим cookie, який потрапляє на сервер B. Оскільки сервер B має інший унікальний ключ, вміст cookie визнається недійсним і клієнту повертається повідомлення про помилку.
Керувати генерацією цих ключів в ASP.NET можна шляхом налаштування параметрів в розділі machineKey, в файлі web.config. Коли веб-додаток виконується на декількох серверах, вам необхідно налаштувати всі сервери так, щоб вони використовували один і той же попередньо згенерований ключ.
Іншою проблемою, пов'язаною з горизонтальним масштабуванням і унікальними ключами, є можливість шифрування розділів в файлах web.config. Закрита інформація в файлах web.config часто шифрується, коли додаток розгортається на серверах. Наприклад, розділ connectionString можна зашифрувати, щоб запобігти витоку імені користувача і пароля до бази даних. Замість того, щоб шифрувати файл web.config на кожному сервері окремо, ускладнюючи процес розгортання, можна згенерувати один зашифрований файл web.config і розгорнути його на всіх серверах. Для цього слід створити RSA-контейнер ключів і імпортувати його на все веб-сервери.
Більш повну інформацію про створення унікальних ключів і включення їх в настройки додатків можна отримати в базі знань Microsoft Knowledge Base. За додатковою інформацією про створення RSA-контейнера ключів звертайтеся до статті «Імпорт і експорт захищених контейнерів ключів RSA для конфігурації» на сайті MSDN.