在Ruby进程,循环之间进行通信

时间:2015-01-07 22:39:32

标签: ruby multithreading process fork

我有一个Ruby应用程序必须全天候运行以处理Web API的信息,这两个API都在Debian实例上的Google Compute Engine上运行 - 该API由Sinatra提供。当我在loop中运行此脚本时,它会耗尽1核vCPU。使用像RabbitMQ这样的消息排队系统将消息从API传递到后端脚本在我看来,可以跳过本地在Ruby脚本之间进行通信的学习机会。

  1. 如何保持脚本处于休眠状态,即等待指令但不消耗内存99%的CPU?我假设它不会处于无限循环中,但我对此感到难过。

  2. 如何将此消息从一个脚本传递到另一个脚本?我读到了内核#Select和forking of subprocesses,但我没有遇到任何明确或可理解的解决方案。

1 个答案:

答案 0 :(得分:0)

分叉对你来说确实是一个很好的解决方案,你只需要理解三个系统调用就可以很好地利用它:fork(),waitpid()和exec()。我不是一个红宝石的家伙,所以希望我的C式解释能让你有足够的理由填补空白。

fork()的工作方式是由操作系统制作调用进程的虚拟内存空间的逐字节副本,就像调用fork()并创建新的一样将副本放入的内存。这将创建一个具有其父级确切状态的新进程 - 除了子进程'fork()调用返回0,而父进程返回新子进程的PID。这允许子进程知道它是一个子进程,并且父进程知道它的子进程是谁。

fork()复制其调用者的过程映像时, exec()系统调用将其调用者的过程映像替换为其参数指定的全新过程映像。

父进程使用 waitpid()系统调用来等待特定子进程的返回值(通过fork()调用将进程ID返回给父进程) ,然后使用操作系统正确记录进程的完成情况。即使您不需要子进程的返回值,也应该在其上调用waitpid(),这样您就不会最终累积“僵尸进程”。

同样,我不是一个Ruby人,所以希望我的C-like伪代码是有道理的。考虑以下服务器:

while(1) { # an infinite loop
  # Wait for and accept connections from your web API.
  pid = fork(); # fork() returns a process ID number

  # If fork() returns a negative number, something went wrong.
  if(pid < 0) {
    exit(1);
  }

  # If fork() returns 0, this is the child process.
  else if(pid == 0) {
    # Remember that because fork() copies your program's state,
    # you can use variables you assigned before the fork to
    # send to the new process as arguments.
    exec(./processingscript.rb, "processingscript.rb", arg1, arg2, arg3, ...);
  }

  # If fork() returns a number greater than 0 (the PID of the forked
  # child process), this is the parent process.
  else if(pid > 0) {
    childreturnvalue = waitpid(pid); # parent process hangs here until
                                     # the process with the ID number
                                     # pid returns.    
  }
}

以这种方式编写,只有在从Web API收到连接时才会运行CPU密切脚本。它进行处理然后终止,等待再次调用。您还可以为waitpid()指定“no hang”选项,以便您可以同时分叉处理脚本的多个实例,而无需在每次需要等待该脚本的实例完成时挂起服务器。

希望这有帮助!也许知道Ruby的人可以编辑它,使其更加习惯于语言。