【问题标题】:Optimizing firebase functions cold start with expressjs使用 expressjs 优化 firebase 功能冷启动
【发布时间】:2021-06-23 01:53:57
【问题描述】:

我试图弄清楚如何优化我的 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 定义路由时如何实现。

【问题讨论】:

    标签: firebase express google-cloud-platform google-cloud-functions cold-start


    【解决方案1】:

    假设您拆分的每个路由器都在它自己的文件中定义,如下所示:

    // $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);
      }
    }
    

    在您的子功能文件中,您将使用该中间件来创建您的快速应用程序并连接路由。

    // $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
    

    【讨论】:

    • 现在不会为所有其他函数调用加载 express 依赖项,因为我们是在根全局范围内导入它吗?由于 onRequest 框架支持下面的 express api,express 是否已经作为全局范围的一部分加载?
    • 有机会我会在稍后再谈,但是根据您提出问题的方式,您正在为每条路线寻找动态导入,这意味着您拥有一个基于快递的大型云功能,而不是您正在为每个云功能单独寻找动态导入。后者更难支持通过 Node 10 迁移所做的环境变量更改,所以我明天必须深入研究我的笔记。
    • 明白了,无论如何感谢您的帮助。我将问题修改为更清楚一点。如果我正确阅读了上述解决方案,唯一的事情就是全局加载快递,但除此之外,一切都在动态导入之后进行了优化。如果可能的话,让 express 本身动态加载似乎很棘手。
    • @KaneChong 更新了Doug 提出的子功能结构。
    • 在查看环境变量后,似乎 Node 8 云函数的 FUNCTION_NAME 环境变量在以后的 Node 版本中被重命名为 K_SERVICE。似乎better-firebase-functions 软件包在这些年后仍然有效。它允许您将函数导出为文件树中的some-function.func.js,而不是凌乱的索引文件,使用父文件夹作为它们的部署组。
    猜你喜欢
    • 2020-12-23
    • 1970-01-01
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 2019-07-15
    相关资源
    最近更新 更多