使用 expressjs 优化 firebase 函数冷启动
Optimizing firebase functions cold start with expressjs
我试图找出如何优化我的 firebase 函数的冷启动时间。看完this article,想试试看,发现文章专门针对http onRequest函数的基本用法,并没有给出使用express的例子。
A similar question popped up here 但似乎没有明确的答案。我看到文章的作者 Doug 实际上评论了这个问题,他提到为应用程序中的每个路由创建一个动态导入 因为 onRequest() 只允许将应用程序作为唯一的传递论点,但除了在没有 express 应用程序的情况下使用基础 API 之外,我并不完全理解他的意思。理想情况下,我将能够使用 express,这样我就可以更好地控制 api url 路径并使用 express 提供的一些实用程序。
任何人都可以通过 Doug 的例子给我一个如何使用 express 的例子吗?即使我必须为每条路线定义一个新的快递应用程序,我也可以接受。只是不知道如何配置它。
编辑:明确地说,目标是优化冷启动跨越所有函数调用,而不仅仅是http路由调用。根据我的理解,Doug 的示例消除了使用 onRequest 声明的单个路由预加载的导入,但它没有显示在通过 express 定义路由时如何实现。
假设您拆分出的每个路由器都在其自己的文件中定义如下:
// $FUNCTIONS_DIR/routes/some-route-handler.js
import express from "express";
const router = express.Router();
/* ... define routes ... */
export default router;
然后您可以使用此中间件仅在需要时加载每个路由处理程序模块。
function lazyRouterModule(modulePath) {
return async (req, res, next) {
let router;
try {
router = (await import(modulePath)).default;
} catch (err) {
// error loading module, let next() handle it
next(err);
return;
}
router(req, res, next);
}
}
在您的子函数文件中,您将使用该中间件来创建您的 Express 应用程序并连接路由。
// $FUNCTIONS_DIR/fn/my-express.js
import express from "express";
const app = express();
app.use('/api', lazyRouterModule('./routes/api.js'));
app.use('/profiles', lazyRouterModule('./routes/profiles.js'));
export default app;
然后在主函数文件中,按需连接子函数文件:
// $FUNCTIONS_DIR/index.js
import * as functions from 'firebase-functions'
export const myExpress = functions.https
.onRequest(async (request, response) => {
await (await import('./fn/my-express.js')).default(request, response)
});
export const newUserData = functions.firestore.document('/users/{userId}')
.onCreate(async (snap, context) => {
await (await import('./fn/new-user-data.js')).default(snap, context)
});
像这样延迟加载模块时,您需要从一个公共文件中延迟加载 firebase-admin
,这样您就不会多次调用 initializeApp()
。
// $FUNCTIONS_DIR/common/firebase-admin.js
import * as admin from "firebase-admin";
admin.initializeApp();
export = admin;
在任何想要使用 "firebase-admin"
的函数中,您可以使用以下方式从此处导入它:
// $FUNCTIONS_DIR/fn/some-function.js OR $FUNCTIONS_DIR/routes/some-route-handler.js
import * as admin from "../common/firebase-admin";
// use admin as normal, it's already initialized
我试图找出如何优化我的 firebase 函数的冷启动时间。看完this article,想试试看,发现文章专门针对http onRequest函数的基本用法,并没有给出使用express的例子。
A similar question popped up here 但似乎没有明确的答案。我看到文章的作者 Doug 实际上评论了这个问题,他提到为应用程序中的每个路由创建一个动态导入 因为 onRequest() 只允许将应用程序作为唯一的传递论点,但除了在没有 express 应用程序的情况下使用基础 API 之外,我并不完全理解他的意思。理想情况下,我将能够使用 express,这样我就可以更好地控制 api url 路径并使用 express 提供的一些实用程序。
任何人都可以通过 Doug 的例子给我一个如何使用 express 的例子吗?即使我必须为每条路线定义一个新的快递应用程序,我也可以接受。只是不知道如何配置它。
编辑:明确地说,目标是优化冷启动跨越所有函数调用,而不仅仅是http路由调用。根据我的理解,Doug 的示例消除了使用 onRequest 声明的单个路由预加载的导入,但它没有显示在通过 express 定义路由时如何实现。
假设您拆分出的每个路由器都在其自己的文件中定义如下:
// $FUNCTIONS_DIR/routes/some-route-handler.js
import express from "express";
const router = express.Router();
/* ... define routes ... */
export default router;
然后您可以使用此中间件仅在需要时加载每个路由处理程序模块。
function lazyRouterModule(modulePath) {
return async (req, res, next) {
let router;
try {
router = (await import(modulePath)).default;
} catch (err) {
// error loading module, let next() handle it
next(err);
return;
}
router(req, res, next);
}
}
在您的子函数文件中,您将使用该中间件来创建您的 Express 应用程序并连接路由。
// $FUNCTIONS_DIR/fn/my-express.js
import express from "express";
const app = express();
app.use('/api', lazyRouterModule('./routes/api.js'));
app.use('/profiles', lazyRouterModule('./routes/profiles.js'));
export default app;
然后在主函数文件中,按需连接子函数文件:
// $FUNCTIONS_DIR/index.js
import * as functions from 'firebase-functions'
export const myExpress = functions.https
.onRequest(async (request, response) => {
await (await import('./fn/my-express.js')).default(request, response)
});
export const newUserData = functions.firestore.document('/users/{userId}')
.onCreate(async (snap, context) => {
await (await import('./fn/new-user-data.js')).default(snap, context)
});
像这样延迟加载模块时,您需要从一个公共文件中延迟加载 firebase-admin
,这样您就不会多次调用 initializeApp()
。
// $FUNCTIONS_DIR/common/firebase-admin.js
import * as admin from "firebase-admin";
admin.initializeApp();
export = admin;
在任何想要使用 "firebase-admin"
的函数中,您可以使用以下方式从此处导入它:
// $FUNCTIONS_DIR/fn/some-function.js OR $FUNCTIONS_DIR/routes/some-route-handler.js
import * as admin from "../common/firebase-admin";
// use admin as normal, it's already initialized