google cloud pubsub node.js客户端与谷歌云功能不兼容

时间:2018-02-05 13:44:52

标签: node.js google-cloud-functions google-cloud-pubsub

架构:

我们有一个使用2个pubsub主题/订阅对的架构:

  • 主题T1由cronjob定期触发(例如每5分钟)。订阅S1是我们云功能的触发器。
  • 主题T2充当由我们的某个服务发布的后台作业的队列。每次执行时,云功能都会读取订阅S2,以便为排队的后台作业提供服务。

这使我们可以控制后台作业的服务频率,而不管它们何时被添加到队列中。

云功能(由S1触发)通过pullingS2读取消息。它决定哪些后台作业准备就绪,并在成功为作业提供服务后,确认相关消息。未准备好或失败的作业不会被确认以后再进行维修。

问题:

我们在使用Google官方node.js pubusb client时遇到问题:

  1. 有时候ACK的消息会重新出现(看似无限)。我们在ACK截止日期之前验证了消息已被确认,并且确定我们通过调查我们的日志来调用ack()
  2. 有时在第一次执行后(重新部署函数之后),后续执行永远不会收到新消息。我们可以通过验证stackdriver中未确认的消息计数或重新部署该函数并查看消息得到服务来验证消息是否在订阅S2中排队。
  3. 我们认为谷歌的node.js pubsub客户端存在问题。云功能文档明确指出not start background activities。但是,查看node.js pubsub客户端源,它显然使用超时在后台提供确认。

    谷歌的node.js pubsub客户端与谷歌云功能不兼容吗? Google recommends accessing the service API's only when a client library does not exist or does not meet other needs。是否在云功能“其他需求”中运行客户端,要求我们使用服务API编写自己的客户端?

    尝试解决方法:

    作为“解决方法”,我们尝试延迟执行云功能以允许node.js pubsub客户端中的任何“后台”进程完成,但这并不能始终如一地消除我们的问题。似乎pubsub客户端不是云功能友好的,无法在云功能执行之间停止恢复。

    2018年2月22日更新

    我写了an article on our blog,详细描述了为什么我们以这种方式使用PubSub以及我们如何处理node.js pubsub客户端与云功能不兼容的事实。 / p>

3 个答案:

答案 0 :(得分:1)

你是如何触发你的职能的?

根据docs,如果你的函数正在使用pubsub消息,那么你应该使用pubsub触发器。使用pubsub触发器时,不需要库。只需在函数末尾调用callback(),即可正确确认pubsub消息。

对于您打算做的事情,我认为您当前的架构不是正确的选择。

我会使用cron task将您的第一步移至Google App Engine,并使此任务只是将消息从T2移至T1,使该函数具有触发器{{1并处理消息。

因此,您的工作将在S2上发布,并且您有一个由cron任务触发的拉取订阅T2的GAE应用,此应用会重新发布该消息S2。然后,您的函数将由订阅T1到主题S1触发,并将在消息中运行该作业,从而避免导入pubsub库的额外处理,并按预期使用该产品。 / p>

此外,我不确定您最初是如何将作业发布到该主题的,但Task Queues是速率限制任务的良好GAE(和product-agnostic in Alpha)选项。

仅用于此的GAE应用程序(设置1个最大实例)将在always free limit内,因此成本不会显着增加。

答案 1 :(得分:1)

来自node.js pubsub客户端confirmed的开发人员使用客户端从云功能中提取消息不是受支持的用例。

另一种方法是使用service APIs。但是,尝试从订阅中提取所有消息时,REST API有their own caveats

答案 2 :(得分:0)

我遇到了同样的问题,我想更好地控制.ack()。查看Google的nodejs库,可以选择重构ack()以返回诺言,以便函数可以等待ack()完成。

Subscriber.prototype.ack_ = function(message) {
  var breakLease = this.breakLease_.bind(this, message);

  this.histogram.add(Date.now() - message.received);

  if (this.writeToStreams_ && this.isConnected_()) {
    this.acknowledge_(message.ackId, message.connectionId).then(breakLease);
    return;
  }

  this.inventory_.ack.push(message.ackId);
  this.setFlushTimeout_().then(breakLease);
};