为什么 Sinatra 请求需要 EM 线程?

问题描述:

Sinatra 应用程序接收对长时间运行任务的请求并 EM.defer,在 EM 的 20 个线程的内部池中启动它们.当运行的EM.defer超过20个时,通过EM.defer存储在EM的线程队列中.

Sinatra app receives requests for long running tasks and EM.defer them, launching them in EM's internal pool of 20 threads. When there are more than 20 EM.defer running, they are stored in EM's threadqueue by EM.defer.

然而,似乎 Sinatra 不会为任何请求提供服务,直到有可用的 EM 线程来处理它们.我的问题是,Sinatra 不是假设使用主线程的反应器来服务所有请求吗?为什么我在提出新请求时会在线程队列中看到添加?

However, it seems Sinatra won't service any requests until there is an EM thread available to handle them. My question is, isn't Sinatra suppose to use the reactor of the main thread to service all requests? Why am I seeing an add on the threadqueue when I make a new request?

重现步骤:

Access /track/
Launch 30 /sleep/ reqs to fill the threadqueue
Access /ping/ and notice the add in the threadqueue as well as the delay

重现它的代码:

require 'sinatra'
#monkeypatch EM so we can access threadpools
module EventMachine 
  def self.queuedDefers 
    @threadqueue==nil ? 0: @threadqueue.size 
  end
  def self.availThreads 
    @threadqueue==nil ? 0: @threadqueue.num_waiting
  end
  def self.busyThreads 
    @threadqueue==nil ? 0: @threadpool_size - @threadqueue.num_waiting
  end   
end 
get '/track/?' do
  EM.add_periodic_timer(1) do 
    p "Busy: " + EventMachine.busyThreads.to_s + "/" +EventMachine.threadpool_size.to_s + ", Available: " + EventMachine.availThreads.to_s + "/" +EventMachine.threadpool_size.to_s + ", Queued: " + EventMachine.queuedDefers.to_s 
  end 
end

get '/sleep/?' do
  EM.defer(Proc.new {sleep 20}, Proc.new {body "DONE"})
end

get '/ping/?' do
  body "pong"
end

我在 Rack/Thin(没有 Sinatra)上尝试了同样的事情并且按预期工作,所以我猜是 Sinatra 造成的.

I tried the same thing on Rack/Thin (no Sinatra) and works as it's supposed to, so I guess Sinatra is causing it.

Ruby version: 1.9.3.p125
EventMachine: 1.0.0.beta.4.1
Sinatra: 1.3.2
OS: Windows

好的,看来 Sinatra 默认以线程模式启动 Thin,导致上述行为.您可以添加

Ok, so it seems Sinatra starts Thin in threaded mode by default causing the above behavior. You can add

set :threaded, false

在您的 Sinatra 配置部分,这将防止 Reactor 在单独的线程上推迟请求,并在负载下阻塞.

in your Sinatra configure section and this will prevent the Reactor defering requests on a separate thread, and blocking when under load.

Source1

Source2