Приложениям, использующим функции первого поколения, следует рассмотреть возможность перехода на функции второго поколения, следуя инструкциям в этом руководстве. Функции второго поколения используют Cloud Run для обеспечения лучшей производительности, лучшей конфигурации, лучшего мониторинга и многого другого.
Примеры на этой странице предполагают использование JavaScript с модулями CommonJS ( require импорт стилей), но те же принципы применимы к JavaScript с ESM ( import … from импорта стилей) и TypeScript.
миграционный процесс
Функции первого и второго поколений могут сосуществовать в одном файле. Это позволяет легко осуществлять пошаговую миграцию по мере готовности. Мы рекомендуем переносить функции по одной, проводя тестирование и проверку перед продолжением.
Проверьте версии Firebase CLI и firebase-function .
Убедитесь, что вы используете как минимум Firebase CLI версии 12.00 и firebase-functions версии 4.3.0 . Более новые версии будут поддерживать как первое, так и второе поколение.
Обновить импорт
Функции второго поколения импортируются из подпакета v2 в SDK firebase-functions . Этот другой путь импорта — всё, что нужно Firebase CLI, чтобы определить, следует ли развертывать код вашей функции как функцию первого или второго поколения.
Подпакет v2 имеет модульную структуру, и мы рекомендуем импортировать только тот конкретный модуль, который вам необходим.
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
После: 2-е поколение
// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
Обновить определения триггеров
Поскольку SDK второго поколения предпочитает модульный импорт, обновите определения триггеров, чтобы отразить изменения в импорте, внесенные на предыдущем шаге.
Изменились аргументы, передаваемые в функции обратного вызова для некоторых триггеров. В этом примере обратите внимание, что аргументы функции обратного вызова onDocumentCreated объединены в один объект event . Кроме того, некоторые триггеры получили новые удобные функции конфигурации, например, параметр cors для триггера onRequest .
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
exports.date = functions.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions.firestore
.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
После: 2-е поколение
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
Используйте параметризованную конфигурацию
Функции второго поколения отказываются от поддержки functions.config в пользу более безопасного интерфейса для декларативного определения параметров конфигурации внутри вашего кода. С новым модулем params CLI блокирует развертывание, если все параметры не имеют допустимого значения, гарантируя, что функция не будет развернута с отсутствующей конфигурацией.
Перейдите на подпакет params
Если вы использовали конфигурацию среды с помощью functions.config , вы можете перенести существующую конфигурацию, преобразовав её в параметризованную конфигурацию . Например:
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
exports.date = functions.https.onRequest((req, res) => {
const date = new Date();
const formattedDate =
date.toLocaleDateString(functions.config().dateformat);
// ...
});
После: 2-е поколение
const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");
const dateFormat = defineString("DATE_FORMAT");
exports.date = onRequest((req, res) => {
const date = new Date();
const formattedDate = date.toLocaleDateString(dateFormat.value());
// ...
});
Миграция вложенной конфигурации
Если в ваших functions.config() использовались вложенные объекты, тип параметра defineJsonSecret предлагает простой способ миграции. Вы можете хранить всю структуру JSON из functions.config() непосредственно в одном секрете.
Например, если бы у вас было:
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
exports.myFunction = functions.https.onRequest((req, res) => {
const apiKey = functions.config().someapi.key;
const webhookSecret = functions.config().someapi.webhookSecret;
// ...
});
Для миграции можно сохранить весь объект someapi в виде строки JSON в секрете с именем SOMEAPI_CONFIG и использовать defineJsonSecret :
После: 2-е поколение
const {onRequest} = require("firebase-functions/v2/https");
const {defineJsonSecret} = require("firebase-functions/params");
const someApiConfig = defineJsonSecret("SOMEAPI_CONFIG");
exports.myFunction = onRequest(<mark>{ secrets: [someApiConfig] }</mark>, (req, res) => {
const apiKey = someApiConfig.value().key;
const webhookSecret = someApiConfig.value().webhookSecret;
// ...
});
Обратите внимание, что, в отличие от functions.config() , любой секрет, определенный с помощью defineSecret или defineJsonSecret должен быть явно привязан к функции с помощью опции { secrets: [...] } , чтобы быть доступным во время выполнения.
Это сохраняет структуру конфигурации и сводит к минимуму изменения в коде. Вы можете использовать Firebase CLI для экспорта существующего JSON-файла functions.config() и установки его в качестве значения для нового секрета. См. раздел «Управление секретами» для получения инструкций по установке значений секретов, включая установку JSON-секретов из файла или стандартного ввода.
Задайте значения параметров
При первом развертывании Firebase CLI запросит все значения параметров и сохранит их в файле dotenv. Чтобы экспортировать значения из файла functions.config , выполните команду firebase functions:config:export .
Для дополнительной безопасности вы также можете указать типы параметров и правила проверки .
Особый случай: API-ключи
Модуль params интегрируется с Cloud Secret Manager, который обеспечивает детальный контроль доступа к конфиденциальным значениям, таким как ключи API. Дополнительную информацию см. в разделах «Секретные параметры» и «Структурированные секреты JSON» .
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
exports.getQuote = functions.https.onRequest(async (req, res) => {
const quote = await fetchMotivationalQuote(functions.config().apiKey);
// ...
});
После: 2-е поколение
const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");
// Define the secret parameter
const apiKey = defineSecret("API_KEY");
exports.getQuote = onRequest(
// make the secret available to this function
{ secrets: [apiKey] },
async (req, res) => {
// retrieve the value of the secret
const quote = await fetchMotivationalQuote(apiKey.value());
// ...
}
);
Задайте параметры выполнения
Изменилась конфигурация параметров среды выполнения между 1-м и 2-м поколениями. Во 2-м поколении также добавлена новая возможность устанавливать параметры для всех функций.
Ранее: 1-е поколение
const functions = require("firebase-functions/v1");
exports.date = functions
.runWith({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
})
// locate function closest to users
.region("asia-northeast1")
.https.onRequest((req, res) => {
// ...
});
exports.uppercase = functions
// locate function closest to users and database
.region("asia-northeast1")
.firestore.document("my-collection/{docId}")
.onCreate((change, context) => {
// ...
});
После: 2-е поколение
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");
// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });
exports.date = onRequest({
// Keep 5 instances warm for this latency-critical function
minInstances: 5,
}, (req, res) => {
// ...
});
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
/* ... */
});
Обновить учетную запись службы по умолчанию (необязательно)
В то время как функции первого поколения используют учетную запись службы Google App Engine по умолчанию для авторизации доступа к API Firebase, функции второго поколения используют учетную запись службы Compute Engine по умолчанию. Это различие может привести к проблемам с разрешениями для функций, перенесенных на второе поколение, в случаях, когда вы предоставили специальные разрешения учетной записи службы первого поколения. Если вы не изменяли разрешения учетной записи службы, вы можете пропустить этот шаг.
Рекомендуемое решение — явно назначить существующую учетную запись службы App Engine по умолчанию первого поколения функциям, которые вы хотите перенести на второе поколение, переопределив значение по умолчанию второго поколения. Это можно сделать, убедившись, что каждая перенесенная функция устанавливает правильное значение для serviceAccountEmail :
const {onRequest} = require("firebase-functions/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions");
// Use the App Engine default service account for all functions
setGlobalOptions({serviceAccountEmail: '<my-project-number>@<wbr>appspot.gserviceaccount.com'});
// Now I use the App Engine default service account.
exports.date = onRequest({cors: true}, (req, res) => {
// ...
});
// I do too!
exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
// ...
});
В качестве альтернативы вы можете убедиться, что изменили данные учетной записи службы таким образом, чтобы они соответствовали всем необходимым разрешениям как для учетной записи службы App Engine по умолчанию (для 1-го поколения), так и для учетной записи службы Compute Engine по умолчанию (для 2-го поколения).
Используйте параллельное выполнение.
Существенным преимуществом функций второго поколения является возможность обработки одним экземпляром функции более одного запроса одновременно. Это может значительно сократить количество холодных запусков, с которыми сталкиваются конечные пользователи. По умолчанию параметр параллельного выполнения установлен на 80, но вы можете установить его на любое значение от 1 до 1000:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// set concurrency value
concurrency: 500
},
(req, res) => {
// ...
});
Настройка параллельного выполнения может повысить производительность и снизить стоимость функций. Подробнее о параллельном выполнении см. в разделе «Разрешить одновременные запросы» .
Проверка использования глобальных переменных
Функции первого поколения, написанные без учета параллельного выполнения, могут использовать глобальные переменные, которые устанавливаются и считываются при каждом запросе. Когда параллельное выполнение включено и один экземпляр начинает обрабатывать несколько запросов одновременно, это может привести к ошибкам в вашей функции, поскольку параллельные запросы начинают устанавливать и считывать глобальные переменные одновременно.
В процессе обновления вы можете установить для своей функции значение ЦП gcf_gen1 и параметр concurrency равным 1, чтобы восстановить поведение первого поколения:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// TEMPORARY FIX: remove concurrency
cpu: "gcf_gen1",
concurrency: 1
},
(req, res) => {
// ...
});
Однако это не рекомендуется в качестве долгосрочного решения, поскольку оно сводит на нет преимущества в производительности функций второго поколения. Вместо этого проведите аудит использования глобальных переменных в ваших функциях и удалите эти временные настройки, когда будете готовы.
Перенаправьте трафик на новые функции второго поколения.
Как и при изменении региона или типа триггера функции , вам потребуется дать функции второго поколения новое имя и постепенно перенаправлять на неё трафик.
Невозможно обновить функцию с первого поколения до второго с тем же именем и запустить firebase deploy . Попытка сделать это приведет к ошибке:
Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.
Прежде чем выполнять эти шаги, убедитесь, что ваша функция идемпотентна , поскольку во время изменений одновременно будут работать как новая, так и старая версии вашей функции. Например, если у вас есть функция первого поколения, которая реагирует на события записи в Firestore, убедитесь, что ответ на запись дважды — один раз функцией первого поколения и один раз функцией второго поколения — обеспечивает согласованное состояние вашего приложения.
- Переименуйте функцию в коде ваших функций. Например, переименуйте
resizeImageвresizeImageSecondGen. - Разверните функцию таким образом, чтобы работали как исходная функция первого поколения, так и функция второго поколения.
- В случае вызываемых функций, очередей задач и HTTP-триггеров, начните направлять всех клиентов на функцию второго поколения, обновив клиентский код, указав имя или URL-адрес функции второго поколения.
- Благодаря фоновым триггерам, функции первого и второго поколений будут реагировать на каждое событие немедленно после развертывания.
- После переноса всего трафика удалите функцию первого поколения, используя команду
firebase functions:deleteинтерфейсе командной строки Firebase.- При желании можно переименовать функцию второго поколения так, чтобы она соответствовала имени функции первого поколения.