Sinatra应用程序接收长时间运行任务的请求,EM.defer它们,在EM的20个线程的内部池中启动它们。当EM.defer运行超过20个时,EM.defer会将它们存储在EM的线程序列中。
但是,似乎Sinatra不会为任何请求提供服务,直到有一个EM线程可以处理它们。我的问题是,是不是Sinatra假设使用主线程的反应器来服务所有请求?当我发出新请求时,为什么我会在线程上看到添加?
重现的步骤:
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正在造成它。
Ruby version: 1.9.3.p125
EventMachine: 1.0.0.beta.4.1
Sinatra: 1.3.2
OS: Windows
答案 0 :(得分:4)
好的,所以看起来Sinatra默认在线程模式下启动Thin会导致上述行为。 你可以添加
set :threaded, false
在您的Sinatra配置部分中,这将阻止Reactor在单独的线程上延迟请求,并在加载时阻止。
答案 1 :(得分:0)
除非我误解了你的问题,否则这几乎就是EventMachine的工作方式。如果您查看docs for EM.defer,则说明:
不要编写将永久阻止的延迟操作。如果是这样的话 当前实现不会检测到问题和线程 永远不会回到游泳池。 EventMachine限制了数量 池中的线程,所以如果你这么做,你的后续 延期的操作将无法运行。
基本上,有一定数量的线程,如果你使用它们,任何挂起的操作都会阻塞,直到一个线程可用。
如果您只需要更多线程,可能会碰到threadpool_size
,但最终这不是一个长期解决方案。
Is Sinatra multi threaded?在这里是一个非常好的问题。简而言之,Sinatra非常棒,但如果你需要合适的线程,你可能需要寻找其他地方。