Рекомендации по повышению производительности и надежности Функций Azure

В этой статье описано, как повысить производительность и надежность бессерверных приложений-функций.

Ниже приведены рекомендации по созданию и разработке бессерверных решений с помощью службы "Функции Azure".

Избегайте длительных функций

Крупные длительные функции могут вызывать непредвиденные проблемы времени ожидания. Подробнее о времени ожидания для заданного плана размещения см. в разделе Длительность времени ожидания приложения-функции.

Функция может быть крупной, если она содержит множество зависимостей Node.js. Импорт зависимостей может также привести к замедлению загрузки, что, в свою очередь, приводит к непредвиденным проблемам времени ожидания. Зависимости можно загрузить явно и неявно. Один модуль, загруженный в коде, может загрузить собственные дополнительные модули.

По возможности выполняйте рефакторинг крупных функций и перерабатывайте их на более мелкие совместимые наборы функций, которые работают сообща и быстро возвращают ответ. Например, для функции веб-перехватчика или триггера HTTP может потребоваться подтверждение в течение ограниченного промежутка времени. Чаще всего для веб-перехватчиков требуется немедленный ответ. Полезные данные триггера HTTP можно передать в очередь для обработки с помощью функции триггера очереди. Такой подход позволяет отложить текущую работу и сразу возвратить ответ.

Взаимодействие функций

Устойчивые функции и Azure Logic Apps используются для управления переходами состояний и обмена данными между несколькими функциями.

Если для интеграции нескольких функций не используются Устойчивые функции или Logic Apps, для обмена данными между функциями лучше всего использовать очереди хранилища. Основная причина в том, что очереди хранилища дешевле и намного проще в подготовке, чем другие варианты хранения.

Размер отдельных сообщений в очереди хранилища ограничен до 64 КБ. Если между функциями нужно передать сообщения большего размера, можно использовать очередь служебной шины Azure, которая поддерживает сообщения размером до 256 КБ на уровне "Стандартный" и 1 МБ на уровне "Премиум".

Если перед обработкой сообщений их нужно отфильтровать, ознакомьтесь со статьями о служебной шине.

Для поддержки обмена крупными сообщениями используются Центры событий.

Создавайте функции без отслеживания состояния

По возможности функции должны быть без отслеживания состояния и идемпотентными. Свяжите все необходимые сведения о состоянии с вашими данными. Например, с обрабатываемым заказом скорее всего будет связан элемент state. Функция может обработать заказ, основываясь на этом состоянии, но в ней самой при этом не отслеживается состояние.

Идемпотентные функции рекомендуется использовать с триггерами таймера. Например, если какую-либо функцию необходимо выполнять раз в день, пропишите ее таким образом, чтобы она выполнялась в любое время в течение дня с тем же результатом. Из функции можно выйти, если в определенный день ничего не нужно делать. Кроме того, если предыдущее выполнение завершилось ошибкой, следующее выполнение должно начаться с прерванного момента.

Создавайте защищенные функции

Предположим, что в любое время в функции может возникнуть исключение. Реализуйте в функции возможность продолжения с предыдущей точки сбоя во время следующего выполнения. Давайте рассмотрим сценарий, в котором необходимо сделать следующее:

  1. Запросить 10 000 строк в базе данных.
  2. Создать сообщение очереди для каждой из этих строк для дальнейшей обработки.

В зависимости от сложности системы, подчиненные службы могут работать неправильно, могут происходить сбои в работе сети или нарушения квот и т. д. Все это может повлиять на вашу функцию в любое время. Функции необходимо подготавливать к таким проблемам.

Как отреагирует ваш код при сбое после вставки 5000 элементов в очередь для обработки? Отслеживайте элементы в наборе, работа с которым завершена. В противном случае их можно вставить позже. Такая двойная вставка может оказать серьезное воздействие на рабочий процесс, поэтому сделайте функции идемпотентными.

Если элемент очереди уже обработан, разрешите холостой цикл выполнения функции.

Воспользуйтесь предоставленными возможностями защиты для компонентов, используемых на платформе Функций Azure. Например, ознакомьтесь с разделом Обработка подозрительных сообщений очереди в документации по триггерам и привязкам очереди службы хранилища Azure.

Рекомендации по организации функций

В рамках вашего решения вы можете разрабатывать и публиковать несколько функций. Эти функции часто объединяются в одно приложение-функцию, но они также могут выполняться в отдельных приложениях-функциях. В планах размещения “Премиум” и “Выделенный” (Служба приложений) несколько приложений-функций также могут совместно использовать одни и те же ресурсы, запуская их в одном плане. Способ группировки функций и приложений-функций может повлиять на производительность, масштабирование, конфигурацию, развертывание и безопасность общего решения. Нет правил, которые применяются к каждому сценарию, поэтому при планировании и разработке функций следует учитывать сведения, приведенные в этом разделе.

Упорядочивание функций для повышения производительности и масштабирования

Каждая создаваемая функция занимает определенный объем памяти. Хотя этот объем обычно невелик, избыток функций в приложении-функции может замедлить запуск приложения на новых экземплярах. Это также означает, что общее использование памяти приложением-функцией может быть выше. Трудно сказать, сколько функций должно быть в одном приложении, это зависит от вашей рабочей нагрузки. Но если функция хранит большой объем данных в памяти, рассмотрите возможность уменьшить число функций в одном приложении.

При запуске нескольких приложений-функций в одном плане “Премиум” или “Выделенный” (Служба приложений) все эти приложения масштабируются вместе. Если одно из ваших приложений-функций предъявляет больше требований к объему памяти, чем другие, оно использует непропорциональное количество ресурсов памяти в каждом экземпляре, где развернуто приложение. Это может уменьшить объем памяти, доступной для других приложений на каждом экземпляре, поэтому может потребоваться запустить приложение-функцию с высоким потреблением памяти в отдельном плане размещения.

Примечание

При использовании плана потребления рекомендуется всегда размещать каждое приложение в собственном плане, так как приложения в любом случае масштабируются независимо друг от друга.

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

Организация функций для настройки и развертывания

У приложений-функций есть файл host.json, который используется для настройки расширенного поведения триггеров функций и среды выполнения Функций Azure. Изменения в файле host.json применяются ко всем функциям в приложении. Если у вас есть функции, для которых требуются пользовательские конфигурации, рассмотрите возможность перемещения их в собственное приложение функцию.

Все функции в локальном проекте развертываются вместе как набор файлов для приложения-функции в Azure. Может потребоваться развернуть отдельно некоторые функций или использовать такие возможности, как слоты развертывания, только для конкретных функций. В таких случаях следует развернуть эти функции (в разных проектах кода) для различных приложений-функций.

Упорядочивание функций по привилегиям

Строки подключения и другие учетные данные, хранящиеся в параметрах приложения, предоставляют всем функциям в приложении-функции одинаковый набор разрешений в связанном ресурсе. Рекомендуется свести к минимуму количество функций с доступом к определенным учетным данным, переместив те из них, которые не используют такие учетные данные, в отдельное приложение-функцию. Для передачи данных между функциями в различных приложениях-функциях всегда можно использовать такие методы, как связывание функций.

Рекомендации по масштабируемости

Способ масштабирования экземпляров приложения-функции зависит от нескольких факторов. Дополнительные сведения см. в документации по масштабированию функций. Ниже приведены рекомендации по оптимальному масштабированию приложения-функции.

Управление подключениями и общий доступ к ним

Используйте повторно подключения к внешним ресурсам, если это возможно. См. раздел Способы управления подключениями в службе "Функции Azure".

Избегание совместного использования учетных записей хранения

При создании приложения-функции необходимо связать его с учетной записью хранения. Подключение к учетной записи хранения сохраняется в параметре приложения AzureWebJobsStorage.

Чтобы увеличить производительность, используйте для каждого приложения-функции отдельную учетную запись хранения. Это особенно важно, если у вас есть устойчивые функции или функции, активируемые концентратором событий, так как и те и другие создают большой объем транзакций с хранилищем. Если логика приложения взаимодействует со службой хранилища Azure напрямую (с помощью пакета SDK службы хранилища) или с помощью одной из привязок к хранилищу, следует использовать выделенную учетную запись хранения. Например, если у вас есть функция, активируемая концентратором событий, которая записывает данные в Хранилище BLOB-объектов, используйте две учетные записи хранения: одну для приложения-функции, а другую — для больших двоичных объектов, сохраняемых функцией.

Не используйте тестовый и рабочий код в одном приложении-функции

Функции в приложении-функции совместно используют ресурсы. Например, память. Если приложение-функция используется в рабочей среде, не добавляйте в нее тестовые функции и ресурсы. Это может вызвать непредвиденные затраты во время выполнения кода в рабочей среде.

Следите за тем, что вы загружаете в рабочие приложения-функции. Память усредняется для каждой функции в приложении.

При наличии общей сборки, указанной в нескольких функциях .NET, поместите ее в общую папку. В противном случае можно случайно развернуть несколько версий одного двоичного файла, которые действуют по-разному в разных функциях.

Не используйте журнал с подробным протоколированием в рабочем коде — это отрицательно сказывается на производительности.

Использование асинхронного кода без блокирующих вызовов

Рекомендуется использовать асинхронное программирование, особенно если вовлечены блокирующие операции ввода-вывода.

В C# никогда не используйте ссылки на свойство Result и не вызывайте метод Wait для экземпляра Task. Применение этого подхода может привести к нехватке потоков.

Совет

Если вы планируете использовать привязки HTTP или веб-перехватчика, спланируйте работу так, чтобы избежать нехватки портов, которая может возникнуть в результате неправильной установки HttpClient. См. дополнительные сведения об управлении подключениями в службе "Функции Azure".

Использование нескольких рабочих процессов

По умолчанию любой экземпляр узла для Функций использует один рабочий процесс. Чтобы повысить производительность, особенно при использовании однопотоковых сред выполнения, таких как Python, задействуйте FUNCTIONS_WORKER_PROCESS_COUNT, чтобы увеличить количество рабочих процессов на узел (до 10). Затем Функции Azure пытаются равномерно распределять одновременные вызовы функций между этими рабочими процессами.

FUNCTIONS_WORKER_PROCESS_COUNT применяется к каждому узлу, создаваемому функциями при масштабировании приложения для удовлетворения потребности.

По возможности получайте сообщения в пакетном режиме

Некоторые триггеры, например триггер концентратора событий, позволяют получать сообщения в пакетном режиме в рамках одного вызова. Пакетная обработка сообщений обеспечивает более высокую производительность. Вы можете настроить максимальный размер пакета в файле host.json, как описано в справочной документации по host.json.

При работе с C# тип функций можно изменить на строго типизированный массив. Например, вместо EventData sensorEvent можно использовать сигнатуру метода EventData[] sensorEvent. При работе с другими языками необходимо явно задать свойство кратности, указав в файле function.jsonзначение many. Так вы включите пакетную обработку, как показано здесь.

Настройте поведение узла для обеспечения оптимального параллелизма

Файл host.json в приложении-функции позволяет настраивать среду выполнения узла и поведение триггера. Кроме настройки поведения пакетной обработки, вы можете управлять параллелизмом определенного числа триггеров. Часто настройка этих параметров помогает масштабировать каждый экземпляр согласно требованиям вызванных функций.

Параметры в файле host.json применяются ко всем функциям приложения в пределах одного экземпляра функции. Например, если у вас есть приложение-функция с двумя функциями HTTP, настроенная на обработку 25 запросов maxConcurrentRequests, любой запрос к одному из триггеров HTTP будет считаться одним из общих 25 параллельных запросов. Если это приложение-функцию масштабировать до 10 экземпляров, десять функций смогут эффективно обрабатывать 250 параллельных запросов (10 экземпляров х 25 параллельных запросов для каждого экземпляра).

Другие параметры конфигурации узла см. в статье о конфигурации host.json.

Дальнейшие действия

Дополнительные сведения см. в следующих ресурсах: