目前使用第 1 代函数的应用应考虑按照本指南中的说明迁移到第 2 代函数。第 2 代函数使用 Cloud Run,能够提供性能、配置和监控等诸多方面的改进。
本页面中的示例假定您使用具有 CommonJS 模块(require
式导入)的 JavaScript,不过同样的操作原理也适用于具有 ESM(import … from
式导入)和 TypeScript 模块的 JavaScript。
迁移过程
第 1 代和第 2 代函数可以在同一个文件中共存;这让您可以在方便时,逐步完成迁移。我们建议您一次迁移一个函数,对其进行测试和验证,之后再进行后续迁移。
验证 Firebase CLI 和 firebase-function
版本
确保您至少使用的是 Firebase CLI 12.00
版和 firebase-functions
4.3.0
版。后续所有版本都同时支持第 2 代和第 1 代函数。
更新导入语句
第 2 代函数是从 firebase-functions
SDK 中的 v2
子软件包导入的。Firebase CLI 只需知晓这一导入路径,即可确定是将您的函数代码部署为第 1 代函数还是第 2 代函数。
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");
更新触发器定义
由于第 2 代 SDK 偏好模块化导入,因此请更新触发器定义以反映在上一步中更改的导入项。
对于某些触发器,传递给回调函数的参数发生了变化。请注意,在此示例中,onDocumentCreated
回调函数的参数已被整合到单个 event
对象中。此外,我们还为某些触发器新增了一些便捷的配置功能,例如 onRequest
触发器的 cors
选项。
之前:第 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) => {
/* ... */
});
使用参数化配置
第 2 代函数不再支持 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());
// ...
});
设置参数值
首次部署时,Firebase CLI 会提示您输入所有参数值,并将这些值保存在 dotenv 文件中。如需导出 functions.config 值,请运行 firebase functions:config:export
。
特殊情况:API 密钥
params
模块与 Cloud Secret Manager 集成,后者提供对 API 密钥等敏感值的精细访问权限控制。如需了解详情,请参阅 Secret 参数。
之前:第 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) => {
/* ... */
});
使用并发设置
第 2 代函数的一大优势是使用单个函数实例就能够同时处理多个请求;这会大幅减少最终用户遭遇冷启动的次数。并发设置的默认值为 80,但您可以将其设为 1 到 1,000 之间的任何值:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// set concurrency value
concurrency: 500
},
(req, res) => {
// ...
});
通过合理调整并发设置,可以提高性能并降低函数费用。如需详细了解并发设置,请参阅允许并发请求。
审核全局变量使用情况
第 1 代函数在编写时并未考虑并发设置,它们可能会使用为每个请求设置和读取的全局变量。启用并发设置并开始使用单个实例同时处理多个请求时,便可能会导致您的函数出错,因为这些并发请求会同时设置和读取全局变量。
升级时,您可以将函数的 CPU 设为 gcf_gen1
,并将 concurrency
设为 1,这样便可恢复第 1 代函数的行为:
const {onRequest} = require("firebase-functions/v2/https");
exports.date = onRequest({
// TEMPORARY FIX: remove concurrency
cpu: "gcf_gen1",
concurrency: 1
},
(req, res) => {
// ...
});
但是,我们不建议将此作为长期的修复方案,因为这放弃了第 2 代函数的性能优势。建议您审核函数中全局变量的使用情况,并在准备就绪后移除这些临时设置。
将流量迁移到新的第 2 代函数
就像更改函数的区域或触发器类型一样,您需要为第 2 代函数指定新名称,并缓慢地向其迁移流量。
无法使用相同名称将函数从第 1 代升级到第 2 代并运行 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 中响应写入事件的第 1 代函数,请确保在响应这些事件时,对写入做出两次响应(一次由第 1 代函数响应,一次由第 2 代函数响应)会使您的应用处于一致的状态。
- 在函数代码中重命名函数。例如,将
resizeImage
重命名为resizeImageSecondGen
。 - 部署函数,以便运行原始的第 1 代函数和第 2 代函数。
- 对于 Callable、任务队列和 HTTP 触发器,请使用第 2 代函数的名称或网址更新客户端代码,以开始将所有客户端指向第 2 代函数。
- 使用后台触发器时,第 1 代和第 2 代函数会在部署后立即响应每个事件。
- 完成所有流量迁移后,使用 Firebase CLI 的
firebase functions:delete
命令删除第 1 代函数。- (可选)重命名第 2 代函数以匹配第 1 代函数的名称。