如何在Ruby中控制(启动/停止)后台进程(服务器应用程序)

9
我正在尝试通过Ruby设置一个服务器进行集成测试(实际上是规格),但无法弄清楚如何控制该进程。
所以,我要做的是:
1. 运行我的gem的rake任务来执行集成规格。 2. 该任务需要首先启动服务器(我使用webrick),然后运行规格。 3. 执行规格后,它应该杀死webrick,以便我不会留下一些未使用的后台进程。
Webrick不是必需的,但它包含在Ruby标准库中,因此能够使用它将是很好的选择。
希望有人能够帮忙!
PS。我正在Linux上运行,因此让其适用于Windows并非我当前的主要任务。
3个回答

14

标准的方法是使用系统函数fork(复制当前进程),exec(用可执行文件替换当前进程)和kill(发送信号给进程以终止它)。

例如:

pid = fork do
  # this code is run in the child process
  # you can do anything here, like changing current directory or reopening STDOUT
  exec "/path/to/executable"
end

# this code is run in the parent process
# do your stuffs

# kill it (other signals than TERM may be used, depending on the program you want
# to kill. The signal KILL will always work but the process won't be allowed
# to cleanup anything)
Process.kill "TERM", pid

# you have to wait for its termination, otherwise it will become a zombie process
# (or you can use Process.detach)
Process.wait pid

这应该适用于任何类Unix系统。Windows以不同的方式创建进程。

非常好,非常感谢。在尝试过程中,我有一个几乎相同的“解决方案”,几乎可以工作,但不幸的是我无法弄清楚它与原来的代码有何不同。此外,在分叉进程后,我需要添加一个等待服务器可用的睡眠时间。谢谢! - rubiii
我能够复现这个差异。我运行的是Kernel#system而不是Kernel#exec。Kernel#exec "通过运行给定的外部命令替换当前进程",而Kernel#system "在子shell中执行命令"。 - rubiii
即使 Ruby 脚本退出,子进程能否保持活动状态? - Tamer Shlash

2

我之前也遇到过类似的问题,以下是我的解决方案。Michael Witrant的回答给了我启示,但我做了一些改动,比如使用Process.spawn代替fork(更新更好的方法)。

# start spawns a process and returns the pid of the process
def start(exe)
    puts "Starting #{exe}"
    pid = spawn(exe)
    # need to detach to avoid daemon processes: http://www.ruby-doc.org/core-2.1.3/Process.html#method-c-detach
    Process.detach(pid)
    return pid
end

# This will kill off all the programs we started
def killall(pids)
  pids.each do |pid|
      puts "Killing #{pid}"
      # kill it (other signals than TERM may be used, depending on the program you want
      # to kill. The signal KILL will always work but the process won't be allowed
      # to cleanup anything)
      begin
        Process.kill "TERM", pid

        # you have to wait for its termination, otherwise it will become a zombie process
        # (or you can use Process.detach)
        Process.wait pid
      rescue => ex
        puts "ERROR: Couldn't kill #{pid}. #{ex.class}=#{ex.message}"
      end
  end
end

# Now we can start processes and keep the pids for killing them later
pids = []
pids << start('./someprogram')

# Do whatever you want here, run your tests, etc. 

# When you're done, be sure to kill of the processes you spawned
killall(pids)

大概就是这些了,试一下并让我知道它的工作情况。


0

我曾经尝试使用fork,但当ActiveRecord涉及到两个进程时会出现问题。我建议使用Spawn插件(http://github.com/tra/spawn)。它只进行fork操作,但可以处理ActiveRecord。


如果您需要AR,则您的解决方案可能是可行的。非常感谢您。但我并不真正需要使用AR。 - rubiii

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