Поделиться через


Повышение производительности и надежности Azure Functions

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

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

Избегайте длительных по времени выполнения функций

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

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

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

Убедитесь, что фоновые задачи завершены

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

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

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

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

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

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

Service Bus разделы полезны, если перед обработкой требуется фильтрация сообщений.

Центры событий полезны для поддержки больших объемов обмена данными.

Напишите функции, которые не сохраняют состояние

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

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

Создание оборонительных функций

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

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

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

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

Если элемент очереди уже обработан, разрешите вашей функции стать no-op.

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

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

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

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

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

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

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

Замечание

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

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

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

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

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

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

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

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

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

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

Повторное использование подключений к внешним ресурсам по возможности. См. как управлять подключениями в Azure Functions.

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

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

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

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

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

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

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

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

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

Асинхронное программирование рекомендуется, особенно при блокировке операций ввода-вывода.

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

Подсказка

Если вы планируете использовать привязки HTTP или WebHook, предусмотрите действия для избегания исчерпания портов, которое может возникнуть в результате неправильного создания экземпляра HttpClient. Дополнительные сведения см. в разделе Как управлять подключениями в Azure Functions.

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

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

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

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

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

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

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

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

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

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

Дальнейшие шаги

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