Firebase 函数:如何维护 'app-global' API 客户端?

Firebase Functions: How to maintain 'app-global' API client?

如何实现在 Cloud Function 实例 函数调用之间共享的 'app-wide' 全局变量?我想创建一个真正的 'global' 对象,它在我所有函数的生命周期内只初始化一次。

上下文:

我的应用程序的整个后端是 Firestore + Firebase Cloud Functions。也就是说,我混合使用后台 (Firestore) 触发器和 HTTP 函数来实现后端逻辑。此外,我依赖第 3 方定位服务来持续监听来自传感器的位置更新。我只需要一个客户端实例来订阅这些更新。

问题是 Firebase/Google Cloud Functions are stateless,这意味着函数实例不共享 memory/objects/state。如果我调用 functionA、functionB、functionC,将至少创建 3 个 locationService 客户端实例,每个实例分别监听第 3 方服务,因此我们最终会重复调用位置 API 回调.

示例代码:

// index.js
const functions = require("firebase-functions");

exports.locationService = require('./location_service');

this.locationService.initClient();

// define callable/HTTP functions & Firestore triggers
...

// location_service.js
var tracker = require("third-party-tracker-js");

const self = (module.exports = {
    initClient: function () {
        tracker.initialize('apiKey')
        .then((client)=>{
            client.setCallback(async function(payload) {
                console.log("received location update: ", payload)
                // process the payload ...
                // with multiple function instances running at once, we receive as many callbacks for each location update
            })

            client.subscribeProject()
            .then((subscription)=>{
                subscription.subscribe()
                .then((subscribeMsg)=>{
                    console.log("subscribed to project with message: ", subscribeMsg); // success
                });
                // subscription.unsubscribe(); // ??? at what point should we unsubscribe?
            })
            .catch((err)=>{
                throw(err)
            })
        })
        .catch((err)=>{
            throw(err)
        })
    },

});

我意识到我正在尝试做的事情大致相当于在单进程环境中实现守护进程,而且像 Firebase/Google Cloud Functions 这样的无服务器环境似乎并不是为了支持这种需求而设计的因为每个实例都作为自己的进程运行。但我很想听听任何相反的想法和可能的解决方法。

另一个想法...

受此 and the official GCF docs on stateless functions 的启发,我考虑使用 Firestore 来保留一个跟踪器值,使我们能够有条件地初始化 API 客户端。大致是这样的:

// read value from db; only initialize the client if there's no valid subscription
let locSubscriberActive = await getSubscribeStatusFromDb();

if (!locSubscriberActive) {
   this.locationService.initClient();
}

// in `location_service.js`, do setSubscribeStatusToDb(); // set flag to true when we call subscribe(). reset when we get terminated

面临的问题:我在什么时候unset/reset那个值?直觉上,我会在初始化客户端的函数实例获得 recycled/killed 时这样做。但是,似乎无法知道何时 Firebase Cloud Function 实例终止?我到处搜索,但找不到有关如何检测此类事件的文档...

Cloud Functions 完全不支持您尝试执行的操作。重要的是要认识到可以为每个部署的功能分配任意数量的服务器实例。这就是 Cloud Functions 扩展和缩减以经济高效地匹配功能负载的方式。这些实例可能会因任何原因随时终止。您没有指示实例何时终止。

此外,实例在空闲时无法执行任何计算。 CPU 资源在一个函数终止后被限制,并在该实例上调用下一个函数时再次启动。当一个函数没有被主动调用时,你不能有任何“守护进程”代码 运行ning。我不知道你的 locationService 做了什么,但它在函数终止后肯定什么都不做,不管它是如何终止的。

对于任何类型的长运行ning 或类似守护进程的代码,Cloud Functions 都不是合适的产品。相反,您还应该考虑使用另一种产品,让您 运行 24/7 不间断地编码。 App Engine 和 Compute Engine 是可行的替代方案,您必须仔细考虑是否以及如何希望它们的服务器实例随负载扩展。