单线程仍然处理并发请求?

时间:2014-08-22 15:41:06

标签: ruby multithreading thin

Ruby进程是单线程。当我们使用瘦服务器启动单个进程时,为什么我们仍然能够处理并发请求?

require 'sinatra'
require 'thin'
set :server, %w[thin]

get '/test' do
  sleep 2   <----
  "success"
end

什么是可以处理并发请求的内部瘦身?如果是由于事件 - 机器框架,上面的代码实际上是一个不用于EM的同步代码。

1 个答案:

答案 0 :(得分:2)

引用章节:&#34;非阻塞IO /反应堆模式&#34; in http://merbist.com/2011/02/22/concurrency-in-ruby-explained/&#34;这是Twisted,EventMachine和Node.js使用的方法。 Ruby开发人员可以使用EventMachine或 基于EventMachine的Web服务器,如Thin以及EM客户端/驱动程序,可以进行非阻塞异步调用。&#34;

问题的核心是EventMachine.defer  *  用于将阻塞操作集成到EventMachine的控制流程中。  延迟的动作是取第一个参数中指定的块(&#34;操作&#34;)  并安排它在EventMachine维护的内部线程池上进行异步执行。

当操作完成时,它将传递块计算的结果(如果有的话)  回到EventMachine反应堆。  然后,EventMachine调用第二个参数中指定的块来推迟(&#34;回调&#34;),  作为其正常事件处理循环的一部分。

操作块计算的结果作为参数传递给回调。   如果在操作完成后不需要执行任何代码,则可以省略回调参数。  *

基本上,在响应HTTP请求时,服务器执行您编写的, 调用Connecction类中的process方法。 看看$GEM_HOME/gems/thin-1.6.2/lib/thin/connection.rb中的代码:

# Connection between the server and client.
# This class is instanciated by EventMachine on each new connection
# that is opened.
class Connection < EventMachine::Connection
# Called when all data was received and the request
# is ready to be processed.
def process
  if threaded?
    @request.threaded = true
    EventMachine.defer(method(:pre_process), method(:post_process))
  else
    @request.threaded = false
    post_process(pre_process)
  end
end

..这里是一个线程连接调用EventMachine.defer

的地方

反应堆

要查看EventMachine reactor的激活位置 应该遵循程序的初始化: 请注意,对于所有Sinatra应用程序和中间件($GEM_HOME/gems/sinatra-1.4.5/base.rb) 可以使用Thin,Puma,Mongrel或WEBrick将Sinatra应用程序作为自托管服务器运行。

  def run!(options = {}, &block)
    return if running?
    set options
    handler         = detect_rack_handler
  ....

方法 detect_rack_handler 返回第一个Rack :: Handler

 return Rack::Handler.get(server_name.to_s)

在我们的测试中,我们需要 thin ,因此它返回一个Thin机架处理程序并设置一个线程服务器

  # Starts the server by running the Rack Handler.
  def start_server(handler, server_settings, handler_name)
    handler.run(self, server_settings) do |server|
            ....
            server.threaded = settings.threaded if server.respond_to? :threaded=

$GEM_HOME/gems/thin-1.6.2/lib/thin/server.rb

# Start the server and listen for connections.
def start
  raise ArgumentError, 'app required' unless @app

  log_info  "Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
  ...      
  log_info "Listening on #{@backend}, CTRL+C to stop"

  @backend.start { setup_signals if @setup_signals }
end

$GEM_HOME/gems/thin-1.6.2/lib/thin/backends/base.rb

  # Start the backend and connect it.
  def start
    @stopping = false
    starter   = proc do
      connect
      yield if block_given?
      @running = true
    end

    # Allow for early run up of eventmachine.
    if EventMachine.reactor_running?
      starter.call
    else
      @started_reactor = true
      EventMachine.run(&starter)
    end
  end