4

我有一个由 poolboy 工人产生的功能

基本概述:

  • Phoenix Controller 用数据调用 Dispatcher
  • Dispatcher 将数据传递给 Poolboy 工作人员
  • Poolboy 工作人员使用给定数据生成一个新进程以进行处理
  • 新进程使用数据调用系统命令(本例中为 wget)

我遇到的问题是,当我运行 ExUnit 测试时,它一直到生成的进程都很好,我可以输出数据(使用 IO.inspect)。

当我运行 System.cmd("wget".... 时,我在 ExUnit 测试运行时在终端中看到 wget 输出,因此该命令实际上正在运行,但是在该命令之后我所做的任何事情都没有运行。

所以在我的工人中,如果我这样做:

IO.puts "hello"
System.cmd("wget", opts)
IO.puts "world"

然后我看到hello我看到了 wget 的输出,但我没有看到world

如果我做其他事情,例如:

IO.puts "hello"
File.write("/tmp/temp.txt", "test")
IO.puts "world"

然后我看到两者helloworld并且写入了一个文件。

是否有一些关于System.cmd我遗漏的具体内容导致这种情况?当它不在单独的进程中运行时,它工作正常,因此它是进程和 System.cmd 的组合。

有任何想法吗?谢谢!

4

2 回答 2

6

您已进入 Elixir 标记的部分

《这里是龙》

System.cmd只是一个简单的包装器,Port并且Port是 Erlangport函数的大部分未记录的包装器。

http://www.erlang.org/doc/man/erlang.html#open_port-2

底层 Erlang BEAM 进程调度器是建立在它可以在非常短的时间间隔内“交换”进程的假设之上的。如果你只使用 Erlang/Exilir 代码,它们都被设计为在 BEAM VM 中工作。任何可能阻塞或挂起系统调用的代码都需要在驱动程序中运行。这是 Erlang VM 的一个特殊接口,它将 Erlang 调度程序与任何可能挂在系统调用上的进程隔离开来。

Ports 驱动程序设置为处理对外部程序的调用。

System.cmd最终调用

 do_cmd Port.open({:spawn_executable, cmd}, opts), initial, fun

端口在一个单独的进程中运行,并且do_cmd例程运行一个接收循环,直到它从底层 Erlang 端口接收到退出状态。因此 System.cmd 将“阻止”该特定 BEAM 进程,直到 wget unix 进程退出。

但是,Elixir BEAM 的其余流程将继续进行。我对 PoolBoy 不够熟悉,不知道您的工作人员是否有某种超时监视器或心跳。但是,如果存在并且 wget 命令超过此超时,则工作进程可能会在 wget 命令完成之前退出。

System.cmd并没有真正设置来处理可能需要很长时间的命令周围的所有问题。我建议您将该Porcelain模块视为围绕相当复杂的 Erlang 端口主题的一个很好的包装器。

https://github.com/alco/porcelain

或者因为你正在做一个简单的 wget,使用 Elixir 或 Erlang HTTP 客户端模块可能会在 BEAM 框架中工作得更好。

于 2015-06-18T22:56:02.540 回答
0

我有同样的问题,并且能够通过运行带有 -q 选项的 wget 命令来回避它。

System.cmd("wget", ["-q", url])

通过使 wget 的输出安静,这似乎可以防止进程卡住。

于 2015-07-09T07:38:57.727 回答