Heroku worker dyno上进程之间的TCP Socket通信

Posted

技术标签:

【中文标题】Heroku worker dyno上进程之间的TCP Socket通信【英文标题】:TCP Socket communication between processes on Heroku worker dyno 【发布时间】:2012-03-08 12:27:43 【问题描述】:

我想知道如何在 Heroku 工作人员测功机上的进程之间进行通信。

我们希望 Resque 工作人员读取队列并将数据发送到在同一个测功机上运行的另一个进程。 “其他进程”是一个现成的软件,通常使用 TCP 套接字(端口 xyz)来侦听命令。它被设置为在 Resque 工作进程启动之前作为后台进程运行。

但是,当我们尝试在本地连接到该 TCP 套接字时,我们一无所获。

我们用于设置队列的 Rake 任务是这样做的:

task "resque:setup" do
  # First launch our listener process in the background
  `./some_process_that_listens_on_port_12345 &`

  # Now get our queue worker ready, set up Redis backing store
  port = 12345
  ENV['QUEUE'] = '*'  
  ENV['PORT'] = port.to_s
  Resque.redis = ENV['REDISTOGO_URL']

  # Start working from the queue
  WorkerClass.enqueue
end

这很有效——我们的侦听器进程运行,Resque 尝试处理排队的任务。但是,Resque 作业失败,因为它们无法连接到 localhost:12345(特别是 Errno::ECONNREFUSED)。

可能,Heroku 在同一个测功机上阻止了 TCP 套接字通信。有没有办法解决这个问题?

我试图将“代码”从这种情况中取出并在命令行上执行(在服务器进程声称它已正确绑定到 12345 之后):

nc localhost 12345 -w 1 </dev/null

但这也不连接。

我们目前正在研究将客户端/服务器代码更改为在双方都使用 UNIXSocket 而不是 TCPSocket,但由于它是现成的软件,如果出现以下情况,我们宁愿避免使用自己的 fork可能。

【问题讨论】:

是否可以使用 Docker 容器解决这个问题? 【参考方案1】:

使用消息队列 Heroku 插件 ...,

喜欢IronMQ 示例

【讨论】:

【参考方案2】:

你试过 Fifo 吗?

http://www.gnu.org/software/libc/manual/html_node/FIFO-Special-Files.html#FIFO-Special-Files

【讨论】:

虽然尼尔的回答是最短的,但最终还是奏效了。 FIFO 文件(命名管道)允许我们将 2 个进程的 stdout 和 stdin 重定向到彼此,创建一个 2-way 通信进程,而无需对网络层做任何事情。是的,我们不得不稍微开放 3rd 方库的源代码(这是我想避免的),但是让任何程序听 stdin 都非常容易! 对不起,我没有完全理解这个答案。您是否在 heroku 的临时文件系统上创建这些特殊的 fifo 文件之一?我认为每个测功机/进程都有自己独立的文件系统,那么它们是如何连接到同一个文件的呢?谢谢。 它们不一定是文件——数据库中的表或redis列表也足够了。 FIFO 文件可以正常工作真是太好了,尼尔——谢谢你的提示——但知道为什么测功机上的(Unix)进程不能通过 TCP 通信吗?我正计划将基于主机名的请求(通过node-http-proxy)代理到两个服务器(Unix)进程,实际上,它不起作用。在这种情况下,重新设计代理和服务以使用 FIFO 文件而不是 TCP/HTTP 是大材小用。我们可以做些什么来支持 Heroku 上的多个端口(内部)侦听?谢谢! 主要问题是您不知道您的其他进程在哪里,或者即使它们存在。您在一个孤立的环境中操作。【参考方案3】:

阅读您的问题,您已经回答了自己的问题,您无法连接到 localhost 12345。

这种设置进程的方式很奇怪,因为您在一个 Heroku dyno 中运行两个进程,这消除了 Heroku 的许多好处,即 independant process scaling、isolation 和 clean depenedency declaration and isolation。

我强烈建议将此作为两个单独的进程运行,它们通过第三方支持服务进行交互。

【讨论】:

感谢 cmets。在这种情况下,客户端和服务器之间存在一对一的关系,因此我相信它们可以独立扩展。事实上,我不认为我想要 1 个服务器和 20 个客户端。我想将协同工作的 2 段代码视为一个单独的、孤立的单元(黑匣子),然后简单地告诉 Heroku,“哟,给我其中的 10 个。”【参考方案4】:

我认为 Heroku 只允许您在每个测功机的给定端口 ($PORT) 中监听。

我在这里看到了两种解决方案:

将 Redis 用作通信中间件,因此工作人员将再次在 Redis 上写入,而侦听器进程将查询 redis 以获取新作业,而不是在端口中侦听。

获取另一个 heroku dyno(或者更好,一个完全不同的应用程序)并在那里启动监听进程(在 $PORT 上)并与两个应用程序通信

【讨论】:

第一个选项可以工作,但我们必须打开服务器并修改代码(不是我想要做的,根据我的原始帖子)。至于第二个,就安全性而言,另一个测功机需要被锁定。 您可以在两个应用程序之间共享一个私有令牌(基本身份验证)【参考方案5】:

@makdad,“第 3 方软件”是用 Ruby 编写的吗?如果是这样,我会用一个猴子补丁来运行它,它会伪造TCPSocket 或它用来访问 TCP 套接字的任何类。将猴子补丁放在它自己的文件中,只有运行第 3 方软件的 Ruby 进程需要。猴子补丁甚至可以直接从队列中读取数据,并使TCPSocket 表现得好像已经收到了数据一样。

是的,它不是很优雅,而且我相信可能有更好的方法来做到这一点,但是你什么时候试图完成工作(而不是花几天时间做研究),有时你只需要咬紧牙关子弹并做一些丑陋但有效的事情。无论您选择何种解决方案,请务必将其记录下来以供以后从事该项目的人员使用。

【讨论】:

Alex,不幸的是它不是用 Ruby 编写的——它实际上是一个用 C++ 编写的编译二进制文件,将 javascript 作为“命令脚本”运行!

以上是关于Heroku worker dyno上进程之间的TCP Socket通信的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Heroku 上使用 Node.js 通信 Web 和 Worker dynos?

Heroku上的rufus-scheduler和delayed_job:为啥要使用worker dyno?

Heroku 主机问题

你能以编程方式访问当前的 Heroku dyno id/name 吗?

AWS Elastic Beanstalk 中的工作人员“dyno”

每月一天限制Heroku dyno