Введение.
В статье рассмотрены понятия Engines, Overrun, Scan Cycle. Выявление состояния переполнения, возможные причины и как избежать переполнения движков.
Что такое Engine?
Application Server (aaEngine.exe) - это 32-битный процесс System Platform, который поочередно выполняет объекты, расположенные на нем.
Application Engine (aaEngine.exe) процесс может быть:
1. Встроен в объект WinPlatform,
2. Приложением AppEngine - движком для планирования и выполнения прикладных объектов,
3. Приложением ViewEngine - движком для нужд визуализации.
Каждый engine процесс имеет уникальное имя в Galaxy и уникальный engine ID в платформе, на которой запущен. Engine, встроенный в WinPlatform всегда имеет ID 1. На рисунке 1 показаны разные типа движков в IDE.
Просмотр детальной информации по движкам доступен в разделе Platform Manager в SMC, как показано на рисунке 2.
Все движки запускают процесс aaEngine.exe в Task Manager (диспетчер задач). Для идентификации процесса воспользуйтесь вкладкой Details Task Manager. Для этого:
1. Откройте Task Manager и перейдите во вкладку Details.
2. Нажмите правой кнопкой мыши по заголовку колонки и выберите Select Columns.
3. Найдите и выберите Command Line.
4. Нажмите OK. Теперь Command Line будет отображаться, как показано на рисунке 3.
В этом примере показан ID = 1 для движка, встроенного в объект WinPlatform.
Scan Cycle (цикл сканирования).
Движки работают циклично. Время, которое движок затрачивает на выполнение всей работы называется периодом сканирования (Scan period).
Назначить Scan period для разных движков (WinPlatform, AppEngine или ViewEngine) можно с помощью свойства Scan Period. Значение задается в мс. На рисунке 4 показан редактор объекта AppEngine.
Рисунок 4 - Конфигурирование Scan period
На рисунке 4 изображен нормальный скан цикл (Scan cycle) движка.
Время выполнения движка (Execution time) может варьироваться от одного цикла к другому в зависимости от загруженности объектов и производительности системных ресурсов. Рекомендуется закладывать среднее время простоя (встроенный атрибут Scheduler.TimeIdleAvg) 50% и более от скан периода движка. Это гарантирует, что у движка достаточно времени, чтобы выполнить все запланированные задачи вовремя.
Если движок не успевает выполнить все задачи внутри одного скан периода, он уйдет в переполнение (overrun) в следующем скан периоде, как показано на рисунке 5.
Примечание: Хотя объекты WinPlatform и
ViewEngine не могут содержать прикладные объекты, они могут иметь
IO атрибуты и скрипты, приводящие к переполнению.
Порядок выполнения объектов.
Порядок выполнения объектов, расположенных на Application Engine показан на рисунке 6.
Объекты выполняются в алфавитном порядке. Однако, вы можете
изменить порядок выполнения для Area и объектов автоматизации, настроив порядок
выполнения объектов (рисунок 7).
Изменение порядка выполнения недоступно для Device Integration объектов, Engine объектов (App и View) и для объектов WinPlatform. Эти объекты всегда выполняются в алфавитном порядке.
Основные причины переполнения движков.
Есть множество причин, по которым движоки могут уйти в
переполнение.
1. Скрипты объектов (Object scripting),
2. Перегрузка (Overloading),
3. Окружение (Environment).
Масштабируемость - преимущество Application Server. Это достигается за счет управления единым пространством имен .NET Framework. Слишком просто написать скрипт, который будет нагружать движок. Сотни и тысячи экземпляров объектов могут привести к этому. Многие проблемы скриптов могут быть исключены.
Application Server позволяет позволяет выбрать вариант выполнения скрипта. По умолчанию, большинство скриптов
имеют тип Execute.
1. Эти скрипты имеют лимит выполнения по сравнению со скриптами Execute. Следовательно, они могут удерживать движок в процессе выполнения.
2. Выполнение скриптовой логики может увеличивать время deploy/undeploy и даже приводить в ошибке.
3. Некоторые данные в скриптах недоступны. Атрибуты объектов запущены на других движках и недоступны в скриптах OnStartup.
В этих скриптах должны использоваться локальные атрибуты и переменные.
Блокировка скриптов
Используйте тип выполнения asynchronously для скриптов, которые могут быть блокированы. Это можно сделать через редактор объекта, как показано на рисунке 8.
Типичным примером скрипта, который может быть блокирован, является SQL скрипт, особенно когда происходит соединение с базой данных, запись/считывание в файл. Эти операции могут превышать скан период движка, особенно если скан период равен 500 мс (значение по умолчанию). Если тип запуска скрипта синхронный (synchronously), движок ожидает завершения скрипта и не продолжает выполнение пока скрипт не завершится. Выполнение осуществляется в основном потоке движка. Эти скрипты могут блокировать движок и приводить движок в Overrun.
В асинхронном скрипте Engine стартует новый поток и выполняет скрипт в нем. Следует обратить внимание на что:
1. Движок не контролирует выполнение скрипта в выделенном потоке.
2. Когда скрипт запущен, движок продолжает выполнение следующего скрипта или задачи, движок не ждет пока скрипт выполнится, чтобы двигаться дальше.
3. Существует лимит по количеству одновременно выполняемых асинхронных скриптов. Это свойство Maximum Asynchronous Thread Count во вкладке General в редакторе Engine. По умолчанию значение равно 5. Это значение может быть увеличено, но с учетом того, что это приводит к созданию новых потоков. Если достигнуто максимальное значение по потокам, следующий скрипт будет ожидать завершение текущего скрипта.
TRY: CATCH блокировка и сбросы триггеров
Любой скрипт, который может привести к исключению должен быть завернут в TRY:CATCH блок.
Пример скрипта без использования TRY:CATCH на рисунке 9.
Здесь есть тонкие моменты, которые можно пропустить. В этом скрипте устанавливается связь с БД SQL из под AppDomain, проверяется может ли БД использоваться и если нет, пишет ошибку в лог. В конце скрипта происходит сброс триггера.
В скрипте может произойти зацикливание: предполагается, что объект SQL Connection возвращен в методе GetData(), а метод возвращает NULL. Следующая строка кода никогда не проверяет этого и использует соединение. Так как возвращается значение NULL, возникает исключение NullReferenceException и скрипт останавливает выполнение в точке. На рисунке 10 показано сообщение, которое появится в логах:
В результате, триггер скрипта cmd_Trigger никогда не возвращается в False и скрипт зацикливается.
Ключевой рекомендацией здесь является включение TRY:CATCH блока в код. Он обрабатывает подобные ситуации и осуществляет очистку (например, избавление от объектов, которые больше не нужны, сброс триггеров скриптов и пр.).
Следует отметить, что хорошим тоном считается:
1. Использование TRY:CATCH блока для кодов, которые могут вызвать исключения (.NET Framework вызовы). Встроенные функции QuickScript, такие как StringToIntg() и т.п. не вызывают исключений.
2. Сброс триггера скрипта вначале скрипта, если скрипт предназначен для однократного выполнения. Если предполагается выполнение скрипта каждый цикл сканирования, убедитесь, что триггер скрипта был сброшен в блоке CATCH, чтобы скрипт не зацикливался в случае возникновения ошибки.
3. Проверка кода. Всегда проверяйте значение NULL при использовании .NET Framework типов в коде. Используйте онлайн документацию, чтобы проверить может ли конкретный метод вызывать исключение и при каких обстоятельствах.
4. Очищение объектов в блоке CATCH при необходимости. Некоторые объекты должны быть освобождены после завершения работы, обычно путем вызова метода Dispose() этого объекта. Поскольку выполнение основной части скрипта останавливается при возникновении исключения и переходит к блоку CATCH, очистку объектов можно случайно пропустить.
5. Использование нескольких блоков TRY:CATCH для сложных сценариев. Это позволяет лучше контролировать ход выполнения скрипта (только той части скрипта, которая невыполнима) и обеспечивает более интуитивный поиск и устранение неисправностей.
Следуя приведенным выше пунктам, ранее упомянутый код можно переписать как показано на рисунке 11:
Косвенные ссылки (Indirect References)
Следует избегать ненужных привязок в скриптах. Например, перебор строк массива, которые содержат ссылки на локальные атрибуты, с последующим использованием функции BindTo() для получения/задания значения. Предпочтительно ссылаться на атрибут напрямую, чем косвенно. Несмотря на то, что привязка к локальному атрибуту (Me.) выполняется быстро, это требует ресурсов.
Оценим эту нагрузку в контексте скрипта, в котором
используется одна косвенная ссылка на локальный атрибут (рисунок 12).
Ниже тот же сценарий, но с использованием прямой ссылки на рисунке 13.
В таблице 2 показано общее количество тактов (TotalTicks)
для сценария с прямой ссылкой.
Как видно из таблиц, прямые ссылки в ~ 19 раз быстрее, чем косвенные (Indirect). Оба скрипта выполняются менее чем за 1 мс. Однако, как показывает этот простой пример, прямая ссылка масштабируется лучше, чем косвенная.
Перегрузка
Скан период и период проверки (Scan Period & Checkpoint Period)
Если приложение
работает медленно, можно предположить, то уменьшение скан периода движка решит
проблему. Однако это не так, так как вы предоставляете движку меньше времени
для выполнения всей рабочей нагрузки перед следующим циклом сканирования. И
если движок еле справляется с нагрузкой за 500 мс, изменение цикла сканирования
до 200 мс только ухудшит положение.
Значения скан периодов для движков на одной и той же платформе должны быть разными. Эта практика не позволяет движкам запускать выполнение одновременно.
Значение Checkpoint period связано со скан периодом. Это значение задается во вкладке General редактора Engine объекта (рисунок 14).
Процедура Checkpoint в Application Server — это процесс сохранения Runtime значений на диск. Это обеспечивает сохранение последних данных сервера приложения в случае потери связи источниками данных или передеплоя объектов. По умолчанию значение Checkpoint period равно 10 000 мс. Этот параметр не должен быть равен 0, в этом случае, сохранение данных будет выполняться при каждом цикле сканирования. Это необоснованно увеличит нагрузку на Engine. Это значение должно быть правильно установлено, так как значение по умолчанию может быть недостаточным для движков с большой нагрузкой.
Частота выполнения скриптов
Частота выполнения скриптов — это важный момент в управлении нагрузкой движка. Выполнение любого скрипта требует ресурсов (ЦП, ОЗУ и т. д.). Довольно безобидный скрипт, запускаемый каждые 5 секунд, может вызвать проблемы с движком.
Задайте себе несколько вопросов, чтобы исключить эти проблемы:
1. Должен ли скрипт выполняться периодически? Может ли он выполняться по событию?
2. Если он должен быть периодическим, должен ли он выполняться каждые N секунд. Можно ли увеличить эту частоту?
3. Должен ли каждый раз выполняться весь скрипт? Могут ли каждый раз выполняться только части скрипта? Сокращение времени выполнения скрипта снижает нагрузку на движок.
4. Подумайте, где работает скрипт. Если этот сценарий является частью базового шаблона и запускается во всех производных экземплярах, обоснованно ли это? Можно ли вместо этого запустить скрипт только в требуемых объектах.
Окружение (Enviroment)
Строго рекомендуется корректно рассчитывать размер серверов приложений (Application Object Servers - AOS). Эта информация дана в документе System Platform Installation Guide.
Рекомендуется заложить 25% для собственных нужд ОС. Недостаточное количество системных ресурсов может быть причиной переполнения движка. Один Engine выполняется на одном ядре процессора. Соотношение движков и ядер 2:1 является максимально рекомендуемым (если включена гиперпоточноть), хотя это зависит от того, насколько сильно загружен каждый движок. Здесь ключевое значение имеет тестирование.
Следует исключить влияние стороннего ПО на систему. Для антивирусного ПО должны быть настроены исключения.
Примечание. Если вы используете нестандартные пути сохранения/пересылки или месторасположение для файлов Checkpoint, их также следует исключить из сканирования.
Не рекомендуется создавать резервные копии узлов AOS, это может влиять на систему. Резервные копии используют технологию Volume Shadow copy Service (VSS). Application Server не поддерживает VSS и это может привести к нестабильности и переполнению движков.
Создание резервной копии AOS не имеет большого смысла, так как дынные не хранятся локально. После восстановления резервной копии с большой вероятностью потребуется повторный деплой на узел, это основная причина, по которой процедура создания резервной копии бессмысленна.
Виртуализация
Нередко можно увидеть перегруженные системы при использовании гипервизора (VMWare, HyperV и т. д.). Настройка виртуального кластера, как правило, передается ИТ-отделам для настройки.
Очень легко попасть в ловушку: "«Ой, машина работает вяло, я просто добавлю больше vCPU (ядер) в систему»"
Причины, по которым возникает эта мысль:
1. Устаревшее мышление, когда приложения запускались на выделенных физических машинах.
2. Простота изменения конфигурации машины в гипервизоре: очень просто добавить vCPU, RAM и т.д.
Все не так, как кажется: виртуальный процессор (vCPU) — это не ядро, это поток и он не сопоставляется напрямую с физическим процессором (pCPU). Добавление дополнительных виртуальных ЦП к виртуальной машине (ВМ) не обязательно улучшит ее работу, может даже ухудшить ее работу.
vCPU - это программное представление потока, которое можно использовать для выполнения команд на CPU. Это не представление физического ядра на ЦП. Это еще больше усиливается технологией Hyper-Threading. Технологию Hyper-Threading можно использовать для увеличения количества ядер, но она влияет на производительность. 8-ядерный ЦП будет работать лучше, чем 4-ядерный ЦП с технологией Hyper-Threading.
Рассмотрим следующий пример показанный на риснке 15:
, где круг синего цвета — это покупатель в очереди, оранжевого - кассир.
В первом сценарии доступен только один кассир, чтобы расплатиться за покупки.
Как можно ускорить процесс? Добавить больше контрольно-пропускных пунктов (рисунок 16)!
, где круг синего цвета — это покупатель в очереди, оранжевого - кассир.
Теперь есть 4 контрольно-пропускных пункта и кассир
вынужден бегать от одной кассы к другой, что приводит к увеличению времени
ожидания.
· Контрольно-пропускной пункт - это поток CPU (vCPU).
· Кассир - это ядро CPU (pCPU).
· Люди, ожидающие оплаты - это инструкции для выполнения на процессоре (CPU).
Проще говоря, нет смысла увеличивать количество виртуальных CPU на виртуальной машине, если на физической стороне их нечем питать, вы просто увеличите время ожидания, поскольку гипервизор должен перенаправлять рабочую нагрузку с различных виртуальных CPU на процессоры.
Для критически важных машин соотношение vCPU:pCPU должно быть 1:1,25 или 1:1. Это можно установить в гипервизоре статически в конфигурации виртуальной машины, либо динамически через DRS :
https://www.vmware.com/uk/products/vsphere/drs-dpm.html .
Атрибуты для мониторинга состояния движка (Engine)
Атрибуты для мониторинга состояния движка можно отслеживать с помощью ObjectViewer, чтобы понять, как работает каждый из ваших Application Engine:
· Engine.AsyncScriptsWaitingCnt - общее количество асинхронных скриптов, которые в настоящее время поставлены в очередь для выполнения на объекте, но еще не выполняются, поскольку они ожидают свободного потока.
· Engine.AsyncScriptThreadMax - максимальное количество потоков асинхронного скрипта, которые могут выполняться одновременно.
· Scheduler.ExecutionTimeAvg - среднее время в миллисекундах, необходимое для выполнения всех объектов за цикл.
· Scheduler.ScanCyclesCnt - количество циклов выполнения с момента последнего сброса статистики.
· Scheduler.ScanOverrunsCnt - количество циклов в Overrun с момента последнего сброса статистики.
· Scheduler.ScanOverrunsConsecCnt - количество последовательных Overrun циклов сканирования. Это значение увеличивается на 1, когда происходит переполнение сканирования. Это значение устанавливается равным 0, когда объект выходит из сканирования.
· Scheduler.TimeIdleAvg - среднее время в миллисекундах за период сканирования, в течение которого объект простаивает.