创建多个轻量级Google云功能的最佳做法?

时间:2017-07-18 09:05:47

标签: google-cloud-functions

Google Cloud Functions的工作方式是:

  • 您的模块进入functions目录
  • functions目录包含一个package.json文件,用于包含所有模块的共享依赖项
  • 模块可以包含每个模块的许多导出功能
  • google云功能和firebase功能对如何处理模块中的导出功能有不同的看法
    • 对于gcp,它似乎这样工作:上传模块,然后通过Web界面或命令行指定应从已加载模块中调用哪个导出方法
    • 对于firebase,它看起来像这样:来自firebase-functions的侦听器方法返回处理程序,但也将触发器元数据附加到该处理程序。然后firebase-tools cli应用程序在本地需要您的代码,获取导出的函数,然后根据其来自firebase函数内容的附加元数据为每个导出的方法创建云函数。因此,如果您将所有云功能放在同一个模块中,那么它将为每个云功能多次部署该模块,并为每个云功能加载整个模块,然后调用特定的导出功能。
  • 如果您将导出的函数配置为http触发器,它将使用未定义的express.js版本,以及无限量的捆绑中间件和
  • 的顺序。

这很奇怪:

  • 说即使模块one.jstwo.js在运行时需要不同的包,它们之间的共享package.json意味着它们的启动时间比单独完成时要慢,因为它们都是需要安装包的所有依赖项而不仅仅是自己的
  • 如果index.js中有多个导出的函数,例如hi()hello(),那么hi云函数也会加载hello()函数尽管没有使用它,但在内存中尽管没有使用它,hello云函数也会在内存中hi(),因为生成的云函数仍将使用相同的index.js文件,即使不需要其他部分,也要将该模块内的所有内容加载到内存中

因此,确保云功能以最轻的运行时足迹运行的最佳实践是什么?由于Google的设计决策似乎意味着您制作的云功能越多,因此每个云功能捆绑的垃圾越多,速度越慢,成本越高。

作为一方,在我看来,这对谷歌来说是一个更好的方法:每个云功能都应该有自己的目录,并且在每个目录中都有一个package.json文件和一个{{1}文件。然后index.js文件执行index.jsmodule.exports = function(...args){}

这种架构符合人们期望云功能运行的方式 - 云功能代表单一功能 - 而不是云功能是在所有云功能之间安装共享依赖关系,然后加载一个可以包含多个云函数但只使用一个的模块,然后只从该加载的模块中执行一个函数。

有趣的是,Azure功能似乎的设计与我希望云功能运行的方式完全相同:https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference-node

2 个答案:

答案 0 :(得分:4)

我们可以通过要求导出特定目录及其子目录中的每个文件的文件,而不是单独导出每个云功能,而不是一次导出所有这些功能。

功能/ index.js
'use strict';

const admin = require('firebase-admin');
const functions = require('firebase-functions');
const logging = require('@google-cloud/logging')();
const stripe = require('stripe')(functions.config().stripe.token);

admin.initializeApp(functions.config().firebase);

module.exports = require('./exports')({
    admin, functions, logging, stripe
});

我们可以为每个提供商创建一个文件夹,例如auth,数据库,https,我们可以在其中组织相关资源的事件,例如auth.user.onCreatedatabase.ref.onWrite

通过在每个文件中使用事件构建我们的文件,我们可以搜索函数文件并使用文件路径动态创建Cloud Function的名称并将其导出。

e.g。 functions/exports/auth/user/onCreate.f.js -> onCreateAuthUser

功能/出口/ index.js
'use strict';

module.exports = (app) => {

    const glob = require('glob');

    glob.sync('./**/*.f.js', { cwd: __dirname }).forEach(file => {

        const only = process.env.FUNCTION_NAME;
        const name = concoctFunctionName(file);

        if (only === undefined || only === name) {
            exports[name] = require(file)(app);
        }
    });

    return exports;
}

function concoctFunctionName(file) {

    const camel = require('camelcase');
    const split = file.split('/');
    const event = split.pop().split('.')[0];
    const snake = `${event}_${split.join('_')}`;

    return camel(snake);
}

最后,我们的函数文件可以使用传递的参数来访问常用的服务,例如Firebase管理和函数,甚至Stripe。

功能/出口/认证/用户/ onCreate.f.js
'use strict';

module.exports = ({ admin, functions, stripe }) => {

    return functions.auth.user().onCreate(event => {

        const { email, uid } = event.data;
        const ref = admin.database.ref.child(`user-customer/${uid}`);

        return stripe.customers.create({ 
            email, metadata: { uid } 
        }).then(customer => {
            return ref.set(customer);
        });
    });
}

答案 1 :(得分:1)

我正在使用modofun(https://modofun.js.org),这是一个基于请求路径的多个操作的路由器。这允许我将相关功能收集到一个部署为单个Google Cloud Function的模块中。该模块中所有函数的依赖关系是相同的,因此使其具有依赖性。您还可以共享共同的全局资源,例如数据库连接。

我同意单独部署每一项功能都是浪费资源,而且很难维护。

我为生产中的Google Cloud Functions部署做了这个。