使用功能框架在本地运行多个Google Cloud功能
#serverless #node #googlecloud #expressjs

因此,您想同时使用Google的functions-framework

您可能以前已经在Firebase中编写了云功能,在该firebase中,Firebase Local Emulator Suite允许您使用单个命令(firebase emulators:start)在单个本地服务器上同时运行所有功能。

该功能框架无法提供可以在此外进行此操作的模拟器。但是,您可以很容易地自己写一个,并且以这种方式近似Firebase的本地发展经验。

这种方法将您的功能仅用于开发目的。您仍然可以将功能分别部署到Google Cloud。

示例设置

在此示例中,我有以下目录结构

├── package.json
└── src
    ├── index.js
    └── functions
        ├── firstFunction.js
        └── secondFunction.js

功能脚本

这两个函数本身都是全面的express.js处理程序,就像它们在firebase中一样。

要测试两个函数可以交互,第一个函数返回http 302重定向,将其重定向到第二个功能上的get请求。

// src/functions/firstFunction.js
export const firstFunction = async (req, res) => {
    res.redirect('/secondFunction');
}
// src/functions/secondFunction.js
export const secondFunction = async (req, res) => {
    res.send("OK! You were redirected here.");
}

package.json

package.jsonsrc/index.js称为主要节点脚本。我们还需要告诉函数框架以定位index.js模块中的index导出:

// package.json

...
"type": "module",
// Tells the functions-framework where to look for exports.
"main": "src/index.js", 
"scripts": {
    "start": "functions-framework --target=index", // Select target export
    "debug": "functions-framework --target=index --debug"
}
...

index.js

index.js文件是此设置的核心。我们将在一个局部地址上公开所有功能,并单独公开功能。

// src/index.js
import express from "express"
import { firstFunction } from "./functions/firstFunction.js";
import { secondFunction } from "./functions/secondFunction.js";

// Solution to expose multiple cloud functions locally
const app = express();
app.use('/firstFunction', firstFunction);
app.use('/secondFunction', secondFunction);


export {app as index, firstFunction, secondFunction};

本地函数框架将针对从index.js导出的index导出,请参见上面的package.jsonindex导出仅用于本地开发目的,因此我们可以在本地一次运行多个功能。

我们仍然导出各个功能,因此我们可以轻松地分别部署两个功能。请参阅以下单独部署功能。

一起运行功能

现在,如果我们运行npm run start,则开发服务器将在http://localhost:8080上启动。

运行curl http://localhost:8080/firstFunction将打印OK! You were redirected here.,证明两个功能都在同一时间运行。

如果您仍然想隔离测试功能,则可以运行functions-framework --target=firstFunction,之后您可以使用curl http://localhost:8080来调用它。

单独部署功能

函数仍然可以单独部署,使用gcloud cli:

gcloud functions deploy firstFunction --project=my-project --runtime nodejs16 --trigger-http --allow-unauthenticated  --security-level=secure-always --region=eyour-region --entry-point=firstFunction --memory=128MB --timeout=60s
gcloud functions deploy secondFunction --project=my-project --runtime nodejs16 --trigger-http --allow-unauthenticated  --security-level=secure-always --region=your-region --entry-point=secondFunction --memory=128MB --timeout=60s

这里的关键是--entrypoint firstFunction标志,它类似于functions-framework命令上的--target标志。它选择了索引脚本的模块导出,该导出应被视为云函数的入口点。

you 可以还将index导出为一个将所有函数结合在一起的单个函数,但是您将在云上调用/index/firstFunction/index/secondFunction,然后您可以t scale或t scale或单独修改函数运行时间。


警告:对req.path和其他快速变量的影响

似乎使用Express``这种方式''不是一种适当的方式,可以同时模拟在Google Cloud中运行的多个单个函数。有一些警告。

req.path 3

如果您需要在应用程序中访问req.path怎么办?这种行为与在Google Cloud中或本地运行单个功能的行为不同吗?

当您作为本地函数运行单个函数,然后在访问该功能中的req.path时在http://localhost:8080/上调用它时,它将产生/。在https://your-google-cloud-domain/firstFunction上调用在Google Cloud中运行的函数的功能也同样存在:访问req.path仍然会产生/,即使您称为实际的URL路径为/firstFunction

我们是否在本地开发环境中正确复制了这种行为?重要的是要在本地开发设置和生产设置之间达到均衡。

答案是。请参阅req.path的快递文档:

从中间件调用时,安装点不包括在req.path中。

当我们致电app.use('/firstFunction', firstFunction);时,我们将firstFunction注册为application-level middleware1

省略点的省略意味着,如果我们运行导出的Express index App(== app),然后我们将http://localhost:8080/firstFunction称为http://localhost:8080/firstFunction,那么它似乎似乎 在其上运行的实际firstFunction函数http://localhost:8080/,而不是http://localhost:8080/firstFunction

req.originalUrlâ

Express Docs

这个属性很像req.url;但是,它保留了原始请求URL,使您可以自由地重写req.url,以实现内部路由。例如,app.use()的安装功能将重写req.url以剥离安装点。

req.originalUrl 不像在真实生产的Google Cloud多功能设置中那样行事。

在Google Cloud上,req.originalUrl不包括函数名称。这可能是由于Google Cloud的一些外部路线。

使用上述索引。

确保您的代码不依赖于某些决策的值req.originalUrl。如果是这样,您可能需要适应此代码。

更多的?

我可能没有其他警告,我对此设置不知道,但就目前而言,它适合我的目的,而当地的开发方便还值得以后的任何并发症。

参考

闭幕说明:此解决方案基于您在研究此问题时可能已经看到的an answer in a related GitHub Issue thread

我写这篇文章是因为该线程包含了与我的用例(以及一些不必要的戏剧)相关的几种方法。我希望开发人员习惯了Firebase函数模型找到此设置建议很有帮助。