简体   繁体   English

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

[英]Start and call Ruby HTTP server in the same script

I wonder how I could start a Ruby Rack application (such as Sinatra) and call it with Net::HTTP or similar in the same script.我想知道如何启动 Ruby 机架应用程序(例如 Sinatra)并在同一脚本中使用 Net::HTTP 或类似名称调用它。 Of couse I could do something like...当然,我可以做类似...

require 'sinatra/base'
require 'net/http'

t = Thread.new do
    class App < Sinatra::Base
        get '/' do
            'Hi!'
        end
    end

    App.run! :host => 'localhost', :port => 1234
end

sleep 2

puts Net::HTTP.start('localhost', 1234) { |http| http.get('/') }.body

t.join

puts 'Bye!'

...but it doesn't feel optimal to sleep for two seconds, waiting for Thin to start. ...但是睡两秒钟,等待 Thin 开始,感觉并不理想。 What I need is some kind of callback when the server has started or does anybody have any other suggestions?我需要的是服务器启动时的某种回调,或者是否有人有任何其他建议?

run! in current Sinatra versions takes a block that is called when the app is started.在当前的 Sinatra 版本中,应用程序启动时会调用一个块。

Using that callback you can do this:使用该回调,您可以执行以下操作:

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')

This seems to be reliable for WEBrick, but when using Thin the callback is still sometimes called a little bit before the server accepts connections.这对于 WEBrick 来说似乎是可靠的,但是当使用 Thin 时,有时在服务器接受连接之前仍然会调用一些回调。

If you look at the run!如果你看run! method in the sinatra source in base.rb you will see this: 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

There is no way to attach callbacks around here.这里没有办法附加回调。 BUT!但! as you see the :running setting is changed once the server is up.如您所见,一旦服务器启动,就会更改:running设置。

So, the simple solution seems to be to have a thread watch App.settings.running in a small polling loop (every 500ms or something along those lines).因此,简单的解决方案似乎是让一个线程在一个小的轮询循环中监视App.settings.running (每 500 毫秒或类似的东西)。 Once running is true you can safely do your stuff.一旦running是真的,你就可以安全地做你的事情了。


Edit: improved version, with a bit of monkey patching goodness.编辑:改进版本,带有一点猴子修补的优点。
Adding an after_running callback to Sinatra:向 Sinatra 添加 after_running 回调:

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

I would use a semaphore (cf. Ruby Semaphores? ) with a capacity of 1 for this task:我会为此任务使用容量为 1 的信号量(参见Ruby 信号量? ):

Main thread:主线程:

  1. Acquire the semaphore获取信号量
  2. Spawn new thread产生新线程
  3. Acquire semaphore (will block until released by the spawned thread)获取信号量(将阻塞直到被生成的线程释放)

Spawned web server thread:衍生 web 服务器线程:

  1. App.run!应用程序运行!
  2. Release semaphore释放信号量

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM