在Heroku工作线程之间进行TCP套接字通信

12
我想了解如何在Heroku工作进程之间进行通信。我们希望Resque工作进程从队列中读取数据并将数据发送到同一个dyno上运行的另一个进程。这个“其他进程”是一款现成的软件,通常使用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正在阻止同一dyno上的TCP套接字通信。有没有办法绕过这个问题?

我尝试把“code”这个情况去掉,只在命令行上执行(在服务器进程声称已正确绑定到12345之后):

nc localhost 12345 -w 1 </dev/null

但这也没有连接成功。

我们目前正在调查更改客户端/服务器代码,使用UNIXSocket代替TCPSocket,但由于它是一款现成的软件,如果可能的话,我们希望避免自己的分支。


使用Docker容器解决这个问题是否可行? - Andrei
5个回答

5

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

例如IronMQ


2

尽管Neil的答案最短,但最终它确实可行。FIFO文件(命名管道)允许我们将2个进程的stdout和stdin互相重定向,创建一个双向通信过程,而不需要处理网络层的任何内容。是的,我们不得不稍微打开第三方库的源代码(这是我想避免的),但让任何程序侦听stdin都非常容易! - makdad
抱歉,我没有完全理解这个答案。您是在Heroku的临时文件系统上创建其中一个特殊的FIFO文件吗?我认为每个dyno / process都保持自己独立的文件系统,那么它们如何连接到同一个文件?谢谢。 - Brian Armstrong
它们不一定需要是文件 - 数据库中的表或Redis列表也足够了。 - Neil Middleton
1
这太棒了,FIFO文件能正常工作,Neil,谢谢你的提示。但是你有没有想过为什么(Unix)进程在dyno上无法通过TCP进行通信呢?我原本计划使用主机名来代理请求(通过node-http-proxy),将其转发给两个服务器(Unix)进程,但事实上,这并不起作用。在这种情况下,重新设计代理和服务以使用FIFO文件而不是TCP/HTTP是过度杀伤力的。我们有什么办法可以在Heroku上支持监听多个端口(内部)吗?谢谢! - Aseem Kishore
主要问题是你不知道其他进程在哪里,甚至不知道它们是否存在。你正在一个孤立的环境中运行。 - Neil Middleton

1

从你的问题来看,你已经回答了自己的问题,无法连接到本地主机12345。

这种设置进程的方式很奇怪,因为你在一个Heroku dyno中运行了两个进程,这就减少了使用Heroku时获得的很多好处,例如独立进程扩展, 隔离干净的依赖声明和隔离

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


1
感谢您的评论。在这种情况下,客户端和服务器之间存在一对一的关系,因此我认为它们可以独立扩展。实际上,我不认为我想要一个服务器和20个客户端。我想将两个代码片段作为单个离散的、隔离的单元(黑盒子)配合工作,并简单地告诉Heroku,“嘿,给我10个这样的”。 - makdad

1
Heroku只允许每个dyno监听给定的端口($PORT),我认为。
我在这里看到两个解决方案:
  • 使用Redis作为通信中间件,这样工作进程会再次写入Redis,而不是在端口上侦听,监听进程会查询新的作业。

  • 获取另一个Heroku dyno(或更好的是完全不同的应用程序),并在那里启动侦听进程($PORT),并通信两个应用程序


第一个选项可能可行,但我们必须打开服务器并修改代码(这不是我想做的,与我的原始帖子不符)。至于第二个选项,另一个动态资源对象需要在安全方面进行锁定。 - makdad
你可以在两个应用之间共享私有令牌(基本身份验证)。 - fuzzyalej

1

@makdad,"第三方软件"是用Ruby编写的吗?如果是,我会使用一个猴子补丁来运行它,这个猴子补丁可以伪造TCPSocket或者其他用于访问TCP套接字的类。将猴子补丁放在一个单独的文件中,这个文件只会被运行第三方软件的Ruby进程所需要。这个猴子补丁甚至可以直接从队列中读取数据,并使TCPSocket表现得好像已经接收到了这些数据。

是的,这并不是非常优雅,我相信可能有更好的方法来解决这个问题,但当你试图完成一项任务(而不是花费数天时间进行研究)时,有时候你只需要咬紧牙关做一些丑陋但有效的事情。无论你选择哪种解决方案,都要确保为以后参与项目的人员记录下来。


Alex,不幸的是它不是用Ruby编写的 - 实际上它是一个用C++编写的编译二进制文件,以“命令脚本”的形式运行Javascript! - makdad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接