【问题标题】:Continue execution after sending response (Cloud Functions for Firebase)发送响应后继续执行(Cloud Functions for Firebase)
【发布时间】:2018-09-19 16:20:06
【问题描述】:

我正在使用带有 https 触发器的 Firebase 函数,我想知道在向客户端发送响应后多久,函数会继续执行。我想向客户端发送响应,然后执行另一个操作(发送邮件)。 目前我正在这样做:

module.exports.doSomeJob = functions.https.onRequest((req, res) => {
    doSomeAsyncJob()
    .then(() => {
        res.send("Ok");
    })
    .then(() => {
        emailSender.sendEmail();
    })
    .catch(...);
});

上面的代码对我有用,但我怀疑代码只是因为在 res.send 完成之前发送邮件已经完成,所以我想知道终止过程究竟是如何工作以确保代码不会中断。

【问题讨论】:

    标签: firebase google-cloud-functions


    【解决方案1】:

    TL;DR

    虽然 https 函数将在 res.send() 之后不久终止,但不能保证 res.send() 之后的 0 行代码将被执行。

    我认为更完整的答案有两个组成部分:

    1. 正如Doug 所指出的,不要在res.send() 之后添加您希望执行的任何附加代码
    2. 云函数将在res.send() 之后不久终止,但不要期望恰好执行 0 行代码

    我遇到了一种情况,对于数据库维护脚本,如果没有记录符合我的条件,我会与 res.send() 说再见,并在其后添加额外的逻辑。我原以为该部分不会运行,因为我已经终止了请求。

    产生意外结果的示例:

    exports.someFunction = functions.https.onRequest((req, res) => {
      if (exitCriteria === true) {
        // we can exit the function, nothing to do
        console.log('Exit criteria met')
        res.status(200).send()
      }
    
      // code to handle if someCriteria was falsy
      console.log('Exit criteria not met, continue executing code')
    })
    

    在上面的例子中,我期待res.send() 立即终止函数 - 事实并非如此,第二个 console.log 也可能会被命中 - 以及您可能拥有的任何其他代码。但是,这并不能保证,因此执行可能会在某个时候突然停止。

    产生正确结果的示例:

    exports.someFunction = functions.https.onRequest((req, res) => {
      if (exitCriteria === true) {
        // we can exit the function, nothing to do
        console.log('Exit criteria met')
        res.status(200).send()
      }
      else {
        // code to handle if someCriteria was falsy
        console.log('Exit criteria not met, continue executing code')
      }
    })
    

    在这个版本中,您将看到 1 行 console.logs - 正如我原本打算的那样。

    【讨论】:

    • 你知道我们是否可以不使用else 而只使用return res.send()
    【解决方案2】:

    如果预期响应与执行结果不挂钩,那么您可以使用

    module.exports.doSomeJob = functions.https.onRequest((req, res) => {
    res.write('SUCCESS')
    return doSomeAsyncJob()
    .then(() => {
        emailSender.sendEmail();
    })
    .then(() => {
        res.end();
    })
    .catch(...);
    });
    

    这会在收到请求后立即返回响应,但会保持函数运行直到调用 res.end() 您的客户端可以在收到回复后立即结束连接,但云功能将继续在后台运行 不确定这是否有帮助,但考虑到执行 pub/sub 需要自己进行一些额外的处理并且需要时间来执行,这可能是客户端需要在非常有限的时间内做出响应的解决方法

    【讨论】:

    • 这太完美了,谢谢!!我的用例是 Slack,如果它在 300 毫秒内没有收到200 状态响应,则会出错。这似乎解决了我遇到的问题。
    • 我很高兴它有帮助
    • 客户端/调用者的连接是否保持活动状态?
    • 这并不总是有效,并且可能会经常失败。 Cloud Functions 会在将响应发送到客户端后立即限制计算资源,并且绝对不能保证任何待处理的异步工作都会完成。
    • 这种假设在实践中并不成立。该函数在响应发送后立即终止。没有所谓的“部分回应”。响应缓存在内存中,并一次性发送。你可能有几分之一秒的时间来计算其他东西,但这不应该依赖。我强烈建议您自己尝试一下,看看它是否真的有效。
    【解决方案3】:

    您应该期望 HTTP 函数在您发送响应后立即终止。任何其他行为都是运气或竞争条件的某种组合。不要写靠运气的代码。

    如果您需要在工作完全完成之前向客户端发送响应,您将需要启动第二个函数以从 HTTP 函数停止的地方继续。通常使用 pub/sub 函数来处理。让 HTTP 函数向另一个函数发送 pub/sub 消息,然后仅在消息发送后终止 HTTP 函数(通过发送响应)。

    【讨论】:

    • Hé 这似乎是唯一的方法。但是,如果我查看 pub/sub 的文档,我似乎只找到了订阅方,而不是如何从云函数中调用它。您能否扩展您的答案以包括如何实际执行此操作?
    • 您可以使用谷歌云发布订阅客户端发送消息。我自己没有做过。 cloud.google.com/nodejs/docs/reference/pubsub/0.16.x
    • 这是对 Google 文档的引用,其中明确说明了这一点,“..发送 HTTP 响应后执行的代码可能随时中断”cloud.google.com/functions/docs/concepts/…
    • @Unnikrishnan 这里有一个很好的例子:stackoverflow.com/a/61525321/867631
    • 此解决方案的问题是发布/订阅可能需要 2 分钟才能触发 - 在生产中看到这一点。
    猜你喜欢
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2022-10-08
    • 2021-02-20
    • 2017-11-16
    相关资源
    最近更新 更多