在同一脚本中启动并调用Ruby HTTP服务器

我想知道如何启动Ruby Rack应用程序(例如Sinatra)并在同一脚本中使用Net :: HTTP或类似方法调用它。 我可以做点什么……

require 'sinatra/base' require 'net/http' t = Thread.new do class App  'localhost', :port => 1234 end sleep 2 puts Net::HTTP.start('localhost', 1234) { |http| http.get('/') }.body t.join puts 'Bye!' 

…但是,等待Thin启动时,睡眠两秒钟并不是最佳选择。 我需要的是当服务器启动时或某人有任何其他建议时的某种回调?

如果你看看run! 在base.rb的sinatra源代码中你会看到这个:

 def run!(options={}) ... handler.run self, :Host => bind, :Port => port do |server| [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } } set :running, true end ... end 

这里没有办法附加回调。 但! 如您所见:running设置在服务器启动后更改。

因此,简单的解决方案似乎是让一个线程在一个小的轮询循环中观察App.settings.running (每隔500毫秒或沿着这些行的东西)。 一旦running是真的,你可以安全地做你的东西。


编辑:改进版本,有一点猴子修补善良。
添加after_running回调给Sinatra:

 class Sinatra::Base # Redefine the 'running' setting to support a threaded callback def self.running=(isup) metadef(:running, &Proc.new{isup}) return if !defined?(after_running) return if !isup Thread.new do Thread.pass after_running end end end class App < Sinatra::Base set :after_running, lambda { puts "We're up!" puts Net::HTTP.start('localhost', 1234) { |http| http.get('/') }.body puts "Done" } get '/' do 'Hi!' end end App.run! :host => "localhost", :port => 1234 

run! 在当前的Sinatra版本中,需要一个在启动应用程序时调用的块。

使用该回调您可以这样做:

 require 'thread' def sinatra_run_wait(app, opts) queue = Queue.new thread = Thread.new do Thread.abort_on_exception = true app.run!(opts) do |server| queue.push("started") end end queue.pop # blocks until the run! callback runs end sinatra_run_wait(TestApp, :port => 3000, :server => 'webrick') 

这对于WEBrick来说似乎是可靠的,但是当使用Thin时,在服务器接受连接之前,有时仍会调用回调。

我会使用一个容量为1的信号量(参见Ruby Semaphores? )来执行此任务:

主线程:

  1. 获取信号量
  2. 产生新线程
  3. 获取信号量(将阻塞直到产生的线程释放)

产生的Web服务器线程:

  1. App.run!
  2. 释放信号量