Советы и усилители; трюки

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

Корректность

В этом разделе описаны общие рекомендации по проектированию и реализации облачных функций.

Напишите идемпотентные функции

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

Не запускать фоновые действия

Фоновая активность — это все, что происходит после завершения вашей функции. Вызов функции завершается, как только функция возвращает значение или иным образом сигнализирует о завершении, например, путем вызова аргумента callback в функциях, управляемых событиями Node.js. Любой код, запущенный после корректного завершения, не может получить доступ к ЦП и не будет выполнять никаких действий.

Кроме того, когда последующий вызов выполняется в той же среде, ваша фоновая активность возобновляется, мешая новому вызову. Это может привести к неожиданному поведению и ошибкам, которые трудно диагностировать. Доступ к сети после завершения функции обычно приводит к сбросу соединений (код ошибки ECONNRESET ).

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

Всегда удаляйте временные файлы

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

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

Не пытайтесь писать за пределами временного каталога и обязательно используйте независимые от платформы/ОС методы для создания путей к файлам.

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

Структура функций

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

Для этого включите предпочитаемую версию в соответствующий файл блокировки (например, package-lock.json для Node.js или requirements.txt для Python).

Инструменты

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

Местное развитие

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

Разработчики Firebase могут использовать эмулятор облачных функций Firebase CLI .

Используйте Sendgrid для отправки электронных писем

Cloud Functions не разрешает исходящие подключения через порт 25, поэтому вы не можете устанавливать незащищенные подключения к SMTP-серверу. Рекомендуемый способ отправки электронных писем — использовать SendGrid . Другие варианты отправки электронной почты можно найти в руководстве по отправке электронной почты из экземпляра для Google Compute Engine.

Производительность

В этом разделе описаны лучшие практики оптимизации производительности.

Используйте зависимости с умом

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

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

Используйте глобальные переменные для повторного использования объектов в будущих вызовах.

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

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

Node.js

console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');

exports.function = functions.https.onRequest((req, res) => {
  console.log('Function invocation');
  const perFunction = lightweightComputation();

  res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});

Питон

import time

from firebase_functions import https_fn

# Placeholder
def heavy_computation():
  return time.time()

# Placeholder
def light_computation():
  return time.time()

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

@https_fn.on_request()
def scope_demo(request):

  # Per-function scope
  # This computation runs every time this function is called
  function_var = light_computation()
  return https_fn.Response(f"Instance: {instance_var}; function: {function_var}")
  

Эта функция HTTP принимает объект запроса ( flask.Request ) и возвращает текст ответа или любой набор значений, который можно превратить в объект Response с помощью make_response .

Особенно важно кэшировать сетевые подключения, ссылки на библиотеки и клиентские объекты API в глобальной области видимости. Примеры см. в разделе «Оптимизация сети» .

Выполните ленивую инициализацию глобальных переменных

Если вы инициализируете переменные в глобальной области видимости, код инициализации всегда будет выполняться посредством вызова холодного запуска, что увеличивает задержку вашей функции. В некоторых случаях это приводит к периодическим тайм-аутам вызываемых служб, если они не обрабатываются должным образом в блоке try / catch . Если некоторые объекты не используются во всех путях кода, рассмотрите возможность их ленивой инициализации по требованию:

Node.js

const functions = require('firebase-functions');
let myCostlyVariable;

exports.function = functions.https.onRequest((req, res) => {
  doUsualWork();
  if(unlikelyCondition()){
      myCostlyVariable = myCostlyVariable || buildCostlyVariable();
  }
  res.status(200).send('OK');
});

Питон

from firebase_functions import https_fn

# Always initialized (at cold-start)
non_lazy_global = file_wide_computation()

# Declared at cold-start, but only initialized if/when the function executes
lazy_global = None

@https_fn.on_request()
def lazy_globals(request):

  global lazy_global, non_lazy_global

  # This value is initialized only if (and when) the function is called
  if not lazy_global:
      lazy_global = function_specific_computation()

  return https_fn.Response(f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}.")
  

Эта функция HTTP использует лениво инициализируемые глобальные переменные. Он принимает объект запроса ( flask.Request ) и возвращает текст ответа или любой набор значений, который можно превратить в объект Response с помощью make_response .

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

Уменьшите количество холодных запусков, установив минимальное количество экземпляров.

По умолчанию Cloud Functions масштабирует количество экземпляров в зависимости от количества входящих запросов. Вы можете изменить это поведение по умолчанию, задав минимальное количество экземпляров, которые Cloud Functions должны поддерживать в готовности к обслуживанию запросов. Установка минимального количества экземпляров снижает вероятность холодного запуска вашего приложения. Мы рекомендуем установить минимальное количество экземпляров, если ваше приложение чувствительно к задержке.

Дополнительные сведения об этих параметрах среды выполнения см. в разделе Поведение управления масштабированием .

Дополнительные ресурсы

Узнайте больше об оптимизации производительности в видеоролике «Атлас производительности Google Cloud» «Облачные функции: время холодной загрузки» .