Easy Firebase Cloud Functions 组织
Posted
技术标签:
【中文标题】Easy Firebase Cloud Functions 组织【英文标题】:Easy Firebase Cloud Functions organization 【发布时间】:2020-12-12 16:13:49 【问题描述】:问题
在研究了 Firebase 文档、视频、***、大量文章之后……以“简单”的方式组织多个(大量)云功能并不明显。特别是,由于 Firebase 官方文档没有提供清晰的愿景/建议。实际上,真正的问题是缺乏关于如何从一开始就设置具有大量功能的 Firebase 项目的清晰文档。
考虑到以下几点,我试图找到一种简单的方法:
基于 Firebase 文档(每个 Firebase 新用户都在阅读) Firebase CLI 部署的一个 index.js 要求 在 TypeScript 上使用纯 javascript,不要使用太多语法魔法糖 对 index.js 中包含/引用的文件/函数的透明控制 开发大量功能,但不要直接导出 index.js 中的所有功能(一种部署控制层) 没有太多的异步/等待导入像这个例子https://medium.com/firebase-developers/organize-cloud-functions-for-max-cold-start-performance-and-readability-with-typescript-and-9261ee8450f0 不依赖于像这个很好的例子中的命名约定https://codeburst.io/organizing-your-firebase-cloud-functions-67dc17b3b0da 在函数数量方面的可扩展性 从初始设置(初学者使用 Firebase 文档)到高级,如何迁移结构? 最大限度地减少添加/部署越来越多云功能的工作量 性能:延迟加载/初始化管理 SDK + 确保它仅被初始化一次 最小化服务器实例冷启动时间 易于解释、易于使用、易于维护(当然有点主观)解决方案?
从手动部署云功能到使用 JavaScript 进行简单的初始 CLI 设置,我尝试了以下文件结构并取得了一些成功:
[project]/
- functions/
- index.js
- src/
- functionA.js
- functionB.js
- ...
...
index.js
结构基于官方文档:https://firebase.google.com/docs/functions/organize-functions
const functions = require('firebase-functions');
const functionA = require('./src/FunctionA');
exports.FunctionA = functionA.FunctionA;
const functionB = require('./src/FunctionB');
exports.FunctionB = functionA.FunctionB;
FunctionA.js
使用https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7#file-index-js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// Prevent firebase from initializing twice
try admin.initializeApp() catch (e) console.log(e);
exports.FunctionA = ...
FunctionB.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// Prevent firebase from initializing twice
try admin.initializeApp() catch (e) console.log(e);
exports.FunctionB = ...
问题
这是一个切实可行的解决方案吗? 与异步/等待导入相比,是否存在任何性能问题?admin.initializeApp()
周围的 try-catch 块是干净的实现吗?
Typescript 等效解决方案是什么?
冷启动时间:通过 Firebase CLI 部署时,我注意到在 Google Cloud Console 的“Cloud Functions”部分中,所有云函数实例都包含所有其他函数的源代码
在云控制台中手动创建函数时,每个函数只有自己的一段代码要部署/初始化/执行
有什么优化建议吗? (例如 index.js 的可维护性)
【问题讨论】:
如果您有 lynda.com 帐户,有一个关于 Firebase 函数的相对较新的优秀课程,其中包含如何设置多个函数的演练。 我刚刚分享了我的 POV in this post,以防您想查看。 【参考方案1】:我会尽量一起回答你的问题:
组织您的函数文件和文件夹是一个见仁见智的问题。您始终可以使用 require() 方法对 index.js 要求其他功能。您无需遵循有关此的官方文档。从another *** question查看这个不错的解决方案
你需要注意的是不要在全局范围内包含其他函数不使用的 require 语句。就像如果您有一个使用 Nodejs 库的函数而另一个函数不使用该库,并且您在全局范围内需要该库,那么获取该库的冷启动将影响这两个函数:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// need the admin sdk
exports.functionA = ...
// doesn't need the admin sdk
exports.functionB = ...
在上面的示例中,获取 admin sdk 的冷启动将适用于这两个功能。您可以通过以下方式对其进行优化:
const functions = require('firebase-functions');
// need the admin sdk
exports.functionA = functions.https.onRequest((request, response) =>
const admin = require('firebase-admin');
...
)
// doesn't need the admin sdk
exports.functionB = ...
我们只在需要它来减少冷启动和优化内存使用的功能中需要 admin sdk。
您甚至可以通过在 typescript 中使用动态导入来进一步改进这一点,它将在调用时获取库或模块代码,而不是在 JS 中使用 require 进行静态导入。
import * as functions from 'firebase-functions'
// this variable will help to initialize the admin sdk once
let is_admin_initialized = false
export const functionA =
functions.https.onRequest(async (request, response) =>
// dynamically import the Admin SDK here
const admin = await import('firebase-admin')
// Only initialize the admin SDK once per server instance
if (!is_admin_initialized)
admin.initializeApp()
is_admin_initialized = true
...
)
关于:
try admin.initializeApp() catch (e) console.log(e);
我相信这是一个有效的实现,这将检查错误:
默认 Firebase 应用已存在。这意味着你打电话给 多次初始化应用程序()而不提供应用程序名称作为 第二个论点。在大多数情况下,您只需要调用 initializeApp() 一次。但是,如果您确实要初始化多个应用程序,请稍等 initializeApp() 的参数,为每个应用指定一个唯一的名称。
除非你想对这个错误做点什么,否则我建议使用上面的实现和一个布尔标志:
is_admin_initialized = false
关于冷启动,在 serverless 架构中是无法避免的弊端,目前还没有办法消除,但可以通过不同的做法来减少:
1- 对于具有 1 个或 2 个依赖项的 JS 函数,您应该或多或少地预期大约 8 秒的冷启动,但这一切都取决于这些包大小的结束。
2- 冷启动可能发生在不同的情况下,例如:
您的功能尚未触发,假设在您的功能部署之后 您的函数实例已关闭,并且没有空闲实例可以接收即将到来的请求 根据我的经验,Cloud Functions 可以让实例闲置几分钟,或多或少大约 10m。 您的函数正在自动扩缩和配置新实例3- 不要包含您的函数不需要的库(依赖项),并且仅使用 JS 中使用 require() 的静态导入或使用 TS 的动态异步导入将依赖项限定为需要它们的函数。请记住,有时您不需要使用整个库,而只需要使用其中的一个功能。在这种情况下,请尝试仅从库中导入该函数,而不是全部导入。
【讨论】:
以上是关于Easy Firebase Cloud Functions 组织的主要内容,如果未能解决你的问题,请参考以下文章
Firebase Cloud Functions 访问 Firebase 托管文件
添加 cloud_firebase 包后,我的 Flutter App 没有运行
Google Cloud Storage 上传是不是会触发 Firebase Cloud 功能?
如何在不使用 Firebase Cloud Messaging 控制台的情况下向 Flutter/Firebase Cloud Messaging 上的特定设备发送通知 [重复]