如何在 Heroku 上使用 Node.js 通信 Web 和 Worker dynos?
Posted
技术标签:
【中文标题】如何在 Heroku 上使用 Node.js 通信 Web 和 Worker dynos?【英文标题】:How to communicate Web and Worker dynos with Node.js on Heroku? 【发布时间】:2012-07-10 21:37:35 【问题描述】:Web Dynos 可以处理 HTTP 请求
而 Web Dynos 可以处理它们,Worker Dynos 可以通过它处理工作。
但我不知道如何让 Web Dynos 和 Worker Dynos 相互通信。
例如,我想接收 Web Dynos
的 HTTP 请求,发送给 Worker Dynos
,处理作业并将结果发送回Web Dynos
,在网络上显示结果。
这在 Node.js 中可行吗? (使用 RabbitMQ 或 Kue 等)?
我在Heroku Documentation 中找不到示例
或者我应该在 Web Dynos 中实现所有代码并仅缩放 Web Dynos?
【问题讨论】:
【参考方案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 插件来共享正在处理的作业的状态或更高性能的状态,例如 Memcache 或 Redis。
因此,总而言之,您必须使用中间附加组件在 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 作为中介(或其他一些队列库)是正确的方法。以上是关于如何在 Heroku 上使用 Node.js 通信 Web 和 Worker dynos?的主要内容,如果未能解决你的问题,请参考以下文章
Node.js - 在 Heroku 上使用 MongoHQ 连接到 MongoDB
无法在 heroku 上使用 node.js 连接到 mongolab
如何将 node.js 应用程序部署到 heroku?有没有可能?