В аналитической части бэкенда Яндекс.Денег активно используется Microsoft IIS, и уже накопился некоторый багаж знаний о его применении в высоконагруженной среде, которым хочется поделиться. Наша аналитика работает на стеке Microsoft (SQL Server и продукты SSIS, SSAS, SSRS) – одном из лучших на рынке BI-решений. Раз в основе нашего BI лежат сервисы одного вендора, то логично и для размещения веб-приложений использовать решение Microsoft – IIS. В статье расскажу о тех особенностях работы с запущенными на IIS приложениями, которые характерны для высоконагруженной среды. Для начала синхронизируем наши с вами представления о «хайлоаде». В качестве иллюстрации высоконагруженной системы в статье используется главным образом наша система защиты от мошенничества (антифрод). Она обрабатывает все входящие операции Яндекс.Денег – а это не одна сотня операций в секунду. Из обработки пользовательского запроса важно исключать логирование, обращения к другим сервисам и все прочее, что не требуется для ответа. Второстепенные действия можно выполнять в параллельных процессах вне основного потока. Особенно критично соблюдение этого правила, когда у сервиса есть некий SLA ответа. Все, что инициализируется «какое-то время», не должно делать этого при поступлении первого запроса от пользователя. Один из наших важнейших сервисов защиты от мошенничества – антифрод – когда-то требовал подгружать весь набор правил при старте каждого нового обработчика запросов.  Кроме того, долгая инициализация чревата каскадной деградацией производительности. Когда приходит первый запрос, антифрод создает под него отдельный обработчик и загружает туда весь список правил безопасности, а пользователь любуется предложением немного подождать. 
Прежний процесс ответа на запросы пользователей при нехватке ресурсов готовых обработчиков. Теперь представьте, что, пока загрузка правил для предыдущего обработчика не завершилась, прилетает запрос на перевод от других пользователей. Аналогичная операция запускается для каждого из них, и таких дублирующихся инициализаций может быть довольно много. Все это приводило к периодическим задержкам интерфейса в 30 секунд и более.  Поэтому объект с правилами сделали единственным и повесили на запуск вместе с основным сервисом. Теперь обработчики запускаются за пару миллисекунд и не влияют на общую производительность сервера (всплески в пределах 10%). Изначально были мысли сделать их запуск заблаговременным, но с учетом быстрого старта каждого обработчика это не оказало бы заметного влияния на производительность. На этом, конечно, не стоит останавливаться – нужно вводить оптимизации на всех уровнях системы. К слову, при обработке правил мы пересмотрели базовый механизм и разделили правила на онлайн и офлайн по логике и серверам, но эта история достойна отдельного рассказа в будущем. Когда речь идет о работе с фермой серверов, нужно или научить приложение привязывать пользовательскую сессию к определенному узлу, или правильно обрабатывать приходящие на разные узлы логически связанные запросы.  Так как привязывать сессию к одному узлу неправильно с точки зрения отказоустойчивости и счастья пользователей, лучше сразу строить систему синхронизации таким образом, чтобы любой из узлов кластера знал о результатах запросов своих «коллег». Сделать это можно разными способами, например: К примеру, мы хотим узнать при проведении платежа, когда был открыт счет-источник. Для этого потребуется или найти саму операцию открытия счета, или сохранить некоторые ее данные отдельно для последующего использования. В нашем случае используется как раз вариант 1. Приложение должно быть всегда запущено, а сам запуск должен производиться заранее, до появления реальных запросов. Кроме того, сервис уже должен быть «прогрет» тестовыми запросами, чтобы сразу отрабатывать входящие с минимальными задержками.  Этому в некоторой степени препятствуют настройки IIS по умолчанию, благодаря которым незагруженные процессы периодически останавливаются. Даже если их автоматически запускать при первом же релевантном запросе извне, приложению потребуется время на первичную инициализацию, обработку связанных запросов и прочее. Поэтому перед стартом процесса или перезапуском веб-приложения стоит переключать нагрузку на другой сервер, дожидаться прогрева вновь запущенного и только после этого включать его в обработку входящих. У IIS есть ограниченный набор служебных потоков для работы с дисками, сетью и пр. Эти потоки используются для обработки большинства пользовательских запросов, и в какой-то момент ресурсов служебных потоков может не хватить. В IIS есть две ключевые настройки потоков, которые могут ограничивать производительность: Queue Length – регулирует длину очереди входящих запросов. Кроме того, есть параметр Пул подключений к БД, который настраивается в клиенте СУБД на стороне веб-приложения. Здесь нужно очень аккуратно выбирать значения, иначе будет множество незадействованных соединений с базой, на каждое из которых расходуется память. Помимо служебных, есть рабочие потоки (WorkerThreads) – как раз те, которые использует серверное приложение для старта новых обработчиков того же антифрода. Этих потоков желательно выдавать ровно столько, сколько требуется для работы. Параметры можно грубо посчитать по следующему алгоритму: Если рассчитать не получается, лучше провести эксперимент в тестовой среде, потому что просто выставить максимальные значения – не лучшая идея. Напрасно потратите ресурсы сервера и увеличите время инициализации служб IIS. Разумеется, этот алгоритм можно рассматривать только как точку старта для оценки именно вашего приложения. Например, не учтены возможные пики нагрузки в вашей среде. Для полноценной оценки потребуется рассмотреть и множество других параметров, которые явно выходят за рамки этой статьи. К слову о ресурсах, стабильной работе 64-битных приложений могут помешать вспомогательные библиотеки, собранные под 32-бита, – вплоть до падения основного приложения. При отладке 64-битных приложений не стоит забывать и о том, что эмулятор IIS из состава Microsoft VisualStudio 2012 (и более ранних) не подойдет, так как он 32-битный и для отладки в 64-битном режиме нужно использовать экземпляр IIS, что чревато проблемами. С точки зрения эксплуатации, для антифрода Яндекс.Денег нужен кластер 1+N, в котором можно вносить изменения только один раз вне зависимости от числа узлов. Поэтому эксперименты в выборе подходящей логики «выкатки» привели к следующему алгоритму: Важно заранее унифицировать структуру размещения приложений на дисках. То есть располагать сайт в одинаковых папках на каждом сервере – это упростит дальнейшую автоматизацию. Для работы фермы IIS потребуется Shared Config для хранения единой конфигурации IIS для всех узлов, который лучше размещать на локальных дисках каждого сервера с синхронизацией скриптами.  Также может потребоваться хранилище общих данных (в нашем случае – правил антифрода). Для этого удобно использовать синхронизацию DFS: приложение работает с локальной папкой на узле, а данные этой папки синхронизируются по остальным машинам средствами DFS, который еще и конфликты сам обрабатывает. Для большей отказоустойчивости и удобства обновления можно использовать схему синхронизации Many-to-Many. Ключевой элемент любой распределенной и высоконагруженной системы – балансировщик входящих запросов. Например, запросов от пользователей к фронтенд-системам. Такой балансировщик может быть двух видов: Использование веб-сервера от Microsoft удобно для тех, кто использует стек приложений и разработки этого же производителя. Если вашему приложению не нужны большие структуры данных, то расходы памяти на хостах будут минимальными. Кроме того, бóльшая часть нужных опций и возможностей предоставляются из коробки, что явно проще сборки аналогов на Opensource.  Если приложение написано на том же .Net – зачем с боем запускать его на «чужой» платформе? Разработчик баз данных