【问题标题】:How to communicate Web and Worker dynos with Node.js on Heroku?如何在 Heroku 上使用 Node.js 通信 Web 和 Worker dynos?
【发布时间】:2012-07-10 21:37:35
【问题描述】:

Web Dynos 可以处理 HTTP 请求

Web Dynos 可以处理它们,Worker Dynos 可以通过它处理工作。

但我不知道如何让 Web DynosWorker Dynos 相互通信。

例如,我想接收 Web Dynos

的 HTTP 请求

,发送给 Worker Dynos

,处理作业并将结果发送回Web Dynos

,在网络上显示结果。

这在 Node.js 中可行吗? (使用 RabbitMQ 或 Kue 等)?

我在Heroku Documentation 中找不到示例

或者我应该在 Web Dynos 中实现所有代码并仅缩放 Web Dynos

【问题讨论】:

    标签: node.js heroku redis rabbitmq kue


    【解决方案1】:

    正如background jobs and queuing 上的高级文章所建议的那样,您的网络测功机将需要通过中间机制(通常是队列)与您的工作测功机进行通信。

    要完成听起来您希望做的事情,请遵循以下一般方法:

    • Web dyno 接收到 Web 请求
    • Web dyno 将作业添加到队列中
    • 工作人员测功机从队列中接收作业
    • Worker dyno 执行作业,将增量进度写入共享组件
    • 浏览器端轮询请求来自 web dyno 的作业状态
      • Web dyno 查询共享组件以获取后台作业的进度并将状态发送回浏览器
    • Worker dyno 完成作业的执行并在共享组件中将其标记为完成
    • 浏览器端轮询请求来自 web dyno 的作业状态
      • Web dyno 查询共享组件以获取后台作业的进度并将完成状态发送回浏览器

    就实际实现而言,我对 Node.js 中最好的库不太熟悉,但是将这个过程粘合在一起的组件可以在 Heroku 上以add-ons 的形式获得。

    队列:AMQP 是一个得到很好支持的队列协议,CloudAMQP 插件可以用作您的网络和工作人员 dynos 之间的消息队列。

    共享状态:您可以使用其中一个 Postgres 插件来共享正在处理的作业的状态或更高性能的状态,例如 MemcacheRedis

    因此,总而言之,您必须使用中间附加组件在 Heroku 上的 dyno 之间进行通信。虽然这种方法涉及更多的工程设计,但其结果是一个适当解耦和可扩展的架构。

    【讨论】:

    • 对此我还有一个问题。当我使用 AMQP 时,您如何保证处理每个作业都由一个工作人员 dyno 处理而不是重复?对我来说,AMQP 类似于 TCP Socket,广播事件并监听事件并做一些事情。如果发生“入队”事件,多个工作人员测功机将对“入队”事件做出反应并尝试同时“出队”事件。我该如何处理这个问题?
    • 虽然队列行为在每个队列和客户端库之间有所不同,但默认行为通常进行广播。因此,默认情况下,当一条消息从队列中被消耗掉时,它是由第一个到达那里的接收者完成的,然后从队列中删除。
    • 在 AMQP 中,您有向其发布消息的 Exchange,并且您有从中获取消息的队列,然后您在它们之间有“绑定”,将消息从 Exchange 路由到一个或多个队列。如果您在 Exchange 和 Queue 之间只有一个绑定(这是默认设置),则可以保证您只会获得发送给该 Queue 的每个订阅者的唯一消息。
    • 此外,AMQP 还有其他不错的好处,例如有序保证,以及消息持久性、高可用性(镜像)队列等功能。(披露,我拥有 CloudAMQP)
    【解决方案2】:

    据我所知,Heroku 没有为您提供交流方式,因此您必须自己构建。为了使用 Node 与另一个进程通信,您可能必须手动处理进程的 stdin/out/err,如下所示:

    var attachToProcess = function(pid) {
        return {
            stdin: fs.createWriteStream('/proc/' + pid + '/fd/0'),
            stdout: fs.createReadStream('/proc/' + pid + '/fd/1'),
            stderr: fs.createReadStream('/proc/' + pid + '/fd/2')
        };
    };
    
    var pid = fs.readFile('/path/to/worker.pid', 'utf8', function(err, pid) {
        if (err) {throw err;}
        var worker = attachToProcess(Number(pid));
        worker.stdin.write(...);
    });
    

    然后,在您的工作进程中,您必须将 pid 存储在该 pid 文件中:

    fs.writeFile('/path/to/worker.pid', process.pid, function(err) {
        if (err) {throw err;}
    });
    

    我还没有实际测试过这些,所以可能需要一些工作和构建,但我认为基本概念很清楚。

    编辑

    只是注意到您也用“redis”标记了它,并认为我应该补充一点,您还可以使用 redis pub/sub 在您的各个进程之间进行通信,如 @987654321 中所述@。

    【讨论】:

    • Heroku dynos 都是虚拟化的,这意味着它们不共享相同的文件系统,即使在同一个应用程序中也是如此。因此,从一个测功机到另一个测功机通过进程 ID 进行通信是行不通的。
    • @RyanDaigle 是的,我认为那里可能存在一些问题。不过,关于 redis 的想法仍然有效。
    • 肯定的。使用 Redis 作为中介(或其他一些队列库)是正确的方法。
    猜你喜欢
    • 2021-02-02
    • 1970-01-01
    • 2012-02-29
    • 2013-04-28
    • 1970-01-01
    • 2017-09-01
    • 2012-04-24
    • 2015-05-04
    • 1970-01-01
    相关资源
    最近更新 更多