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

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

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

В этом разделе описаны общие рекомендации по проектированию и реализации Cloud Functions .

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

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

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

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

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

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

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

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

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

Если вам нужен доступ к долгосрочному хранилищу, рассмотрите возможность подключения томов Cloud Run с томами Cloud Storage или NFS .

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

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

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

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

Если Functions Framework не указан явно как зависимость, она будет автоматически добавлена ​​в процессе сборки с использованием последней доступной версии.

Инструменты

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

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

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

Разработчики Firebase могут использовать эмулятор Cloud Functions Firebase CLI .

Избегайте тайм-аутов развертывания во время инициализации

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

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

Чтобы избежать тайм-аута, используйте одну из следующих стратегий:

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

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});
from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(Альтернативный вариант) Увеличьте время ожидания обнаружения.

Если вы не можете выполнить рефакторинг своего кода для использования onInit() , вы можете увеличить время ожидания развертывания CLI с помощью переменной среды FUNCTIONS_DISCOVERY_TIMEOUT :

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

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

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

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

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

Избегайте низкого параллелизма

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

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

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

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

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

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

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

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

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 в глобальной области видимости. Примеры см. в разделе «Оптимизация сети» .

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

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

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

Примечания о холодном запуске и инициализации

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

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

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

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

Пример предварительного разогрева асинхронной библиотеки node.js

Node.js с Firestore — это пример асинхронной библиотеки node.js. Чтобы воспользоваться преимуществами min_instances, следующий код завершает загрузку и инициализацию во время загрузки, блокируя загрузку модуля.

Используется TLA, что означает, что требуется ES6, используя расширение .mjs для кода node.js или добавляя type: module в файл package.json.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}
Node.js
import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

Примеры глобальной инициализации

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 .

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

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

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

,

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

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

В этом разделе описаны общие рекомендации по проектированию и реализации Cloud Functions .

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

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

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

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

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

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

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

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

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

Если вам нужен доступ к долгосрочному хранилищу, рассмотрите возможность подключения томов Cloud Run с томами Cloud Storage или NFS .

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

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

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

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

Если Functions Framework не указан явно как зависимость, она будет автоматически добавлена ​​в процессе сборки с использованием последней доступной версии.

Инструменты

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

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

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

Разработчики Firebase могут использовать эмулятор Cloud Functions Firebase CLI .

Избегайте тайм-аутов развертывания во время инициализации

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

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

Чтобы избежать тайм-аута, используйте одну из следующих стратегий:

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

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});
from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(Альтернативный вариант) Увеличьте время ожидания обнаружения.

Если вы не можете выполнить рефакторинг своего кода для использования onInit() , вы можете увеличить время ожидания развертывания CLI с помощью переменной среды FUNCTIONS_DISCOVERY_TIMEOUT :

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

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

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

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

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

Избегайте низкого параллелизма

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

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

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

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

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

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

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

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

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 в глобальной области видимости. Примеры см. в разделе «Оптимизация сети» .

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

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

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

Примечания о холодном запуске и инициализации

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

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

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

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

Пример предварительного разогрева асинхронной библиотеки node.js

Node.js с Firestore — это пример асинхронной библиотеки node.js. Чтобы воспользоваться преимуществами min_instances, следующий код завершает загрузку и инициализацию во время загрузки, блокируя загрузку модуля.

Используется TLA, что означает, что требуется ES6, используя расширение .mjs для кода node.js или добавляя type: module в файл package.json.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}
Node.js
import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

Примеры глобальной инициализации

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 .

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

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

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

,

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

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

В этом разделе описаны общие рекомендации по проектированию и реализации Cloud Functions .

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

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

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

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

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

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

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

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

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

Если вам нужен доступ к долгосрочному хранилищу, рассмотрите возможность подключения томов Cloud Run с томами Cloud Storage или NFS .

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

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

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

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

Если Functions Framework не указан явно как зависимость, она будет автоматически добавлена ​​в процессе сборки с использованием последней доступной версии.

Инструменты

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

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

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

Разработчики Firebase могут использовать эмулятор Cloud Functions Firebase CLI .

Избегайте тайм-аутов развертывания во время инициализации

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

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

Чтобы избежать тайм-аута, используйте одну из следующих стратегий:

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

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});
from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(Альтернативный вариант) Увеличьте время ожидания обнаружения.

Если вы не можете выполнить рефакторинг своего кода для использования onInit() , вы можете увеличить время ожидания развертывания CLI с помощью переменной среды FUNCTIONS_DISCOVERY_TIMEOUT :

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

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

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

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

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

Избегайте низкого параллелизма

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

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

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

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

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

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

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

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

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 в глобальной области видимости. Примеры см. в разделе «Оптимизация сети» .

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

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

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

Примечания о холодном запуске и инициализации

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

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

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

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

Пример предварительного нагревания библиотеки Async Node.js

Node.js с Firestore является примером библиотеки Async Node.js. Чтобы воспользоваться преимуществами MIN_INSTANCES, следующий код завершает загрузку и инициализацию во время загрузки, блокируя загрузку модуля.

Используется TLA, что означает, что ES6 требуется, используя расширение .mjs для кода node.js или добавление type: module в файл package.json.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}
Node.js
import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

Примеры глобальной инициализации

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 Performance "Google Cloud Atlas".

,

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

Правильность

В этом разделе описываются лучшие практики для разработки и реализации Cloud Functions .

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

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

Не начинайте фоновые действия

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

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

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

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

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

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

Если вам нужен доступ к долгосрочным хранилищам, рассмотрите возможность использования Mounts Cloud Run Tolume с Cloud Storage или томами NFS .

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

Функции структуры

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

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

Если Functions Framework не указана в качестве зависимости, она будет автоматически добавлена ​​в процессе сборки, используя последнюю доступную версию.

Инструменты

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

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

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

Разработчики Firebase могут использовать эмулятор Firebase CLI Cloud Functions .

Избегайте тайм -аутов развертывания во время инициализации

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

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

Чтобы избежать тайм -аута, используйте одну из следующих стратегий:

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

const { onInit } = require('firebase-functions/v2/core');
const { onRequest } = require('firebase-functions/v2/https');

// Example of a slow initialization task
function slowInitialization() {
  // Simulate a long-running operation (e.g., loading a large model, network request).
  return new Promise(resolve => {
      setTimeout(() => {
          console.log("Slow initialization complete");
          resolve("Initialized Value");
      }, 20000); // Simulate a 20-second delay
  });
}
let initializedValue;

onInit(async () => {
  initializedValue = await slowInitialization();
});

exports.myFunction = onRequest((req, res) => {
  // Access the initialized value. It will be ready after the first invocation.
  res.send(`Value: ${initializedValue}`);
});
from firebase_functions.core import init
from firebase_functions import https_fn
import time

# Example of a slow initialization task
def _slow_initialization():
  time.sleep(20)  # Simulate a 20-second delay
  print("Slow initialization complete")
  return "Initialized Value"

_initialized_value = None

@init
def initialize():
  global _initialized_value
  _initialized_value = _slow_initialization()

@https_fn.on_request()
def my_function(req: https_fn.Request) -> https_fn.Response:
  # Access the initialized value. It will be ready after the first invocation.
  return https_fn.Response(f"Value: {_initialized_value}")

(Альтернатива) Увеличьте тайм -аут обнаружения

Если вы не можете отразить свой код для использования onInit() , вы можете увеличить время ожидания развертывания CLI, используя переменную среды FUNCTIONS_DISCOVERY_TIMEOUT :

$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions

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

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

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

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

Избегайте низкого параллелизма

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

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

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

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

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

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

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

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

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 в глобальном объеме. См. Оптимизируя сеть для примеров.

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

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

См. Поведение управления масштабированием для получения дополнительной информации об этих параметрах времени выполнения.

Примечания о холодном старте и инициализации

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

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

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

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

Пример предварительного нагревания библиотеки Async Node.js

Node.js с Firestore является примером библиотеки Async Node.js. Чтобы воспользоваться преимуществами MIN_INSTANCES, следующий код завершает загрузку и инициализацию во время загрузки, блокируя загрузку модуля.

Используется TLA, что означает, что ES6 требуется, используя расширение .mjs для кода node.js или добавление type: module в файл package.json.

{
  "main": "main.js",
  "type": "module",
  "dependencies": {
    "@google-cloud/firestore": "^7.10.0",
    "@google-cloud/functions-framework": "^3.4.5"
  }
}
Node.js
import Firestore from '@google-cloud/firestore';
import * as functions from '@google-cloud/functions-framework';

const firestore = new Firestore({preferRest: true});

// Pre-warm firestore connection pool, and preload our global config
// document in cache. In order to ensure no other request comes in,
// block the module loading with a synchronous global request:
const config = await firestore.collection('collection').doc('config').get();

functions.http('fetch', (req, res) => {

// Do something with config and firestore client, which are now preloaded
// and will execute at lower latency.
});

Примеры глобальной инициализации

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 Performance "Google Cloud Atlas".