Are rails controllers multithreaded? Thread.exclusive in controllers

后端 未结 3 638
无人及你
无人及你 2020-12-15 12:56

Are Rails controllers multithreaded?

If so, can I protect a certain piece of code (which fires only once every ten minutes) from being run from multiple threads by

相关标签:
3条回答
  • 2020-12-15 13:02

    Ruby is single threaded. So at anytime, a controller can only handle one request at a time. If there are more than one requests, these requests are queued up. To avoid this, people usually run a small set of Mongrels to get good concurrency. It works like this(straight from Mongrel WIKI FAQ):

    1. A request hits mongrel.
    2. Mongrel makes a thread and parses the HTTP request headers
    3. If the body is small, then it puts the body into a StringIO
    4. If the body is large then it streams the body to a temp file
    5. When the request is "cooked" it call the RailsHandler.
    6. The RailsHandler sees if the file is possibly page cached, if so then it sends the cached page.
    7. Now you're finally ready to process the Rails request. LOCK!
    8. Still locked, Mongrel calls the Rails Dispatcher to handle the request, passing in the headers, and StringIO or Tempfile for body.
    9. When Rails is done, UNLOCK! . Rails has (hopefully) put all of its output into a StringIO.
    10. Mongrel then takes this StringIO output, any output headers, and streams them back to the client super fast.

      Notice that if there is no locking if the page is cached.

    0 讨论(0)
  • 2020-12-15 13:09

    My understanding is that a new controller instance is created for each HTTP request that is processed by a controller.

    0 讨论(0)
  • 2020-12-15 13:12

    Running rake middleware on a basic rails app gives the following:

    use Rack::Lock
    use ActionController::Failsafe
    use ActionController::Reloader
    use ActiveRecord::ConnectionAdapters::ConnectionManagement
    use ActiveRecord::QueryCache
    use ActiveRecord::SessionStore, #<Proc:0x017fb394@(eval):8>
    use ActionController::RewindableInput
    use ActionController::ParamsParser
    use Rack::MethodOverride
    use Rack::Head
    run ActionController::Dispatcher.new
    

    The first item on the rack stack is Rack::Lock. This puts a lock around each request, so only one request is handled at a time. As such a standard rails app is single threaded. You can however spawn new threads within a request that would make your app multi threaded, most people never encounter this.

    If you are having issues…

    require 'thread'
    Thread.exclusive do
         # stuff here
    end
    

    … would ensure that stuff inside the block is never run in parallel with any other code. Creating a shared Mutext between all threads (in a class variable or something, but this could be wiped when reloaded in dev mode, so be careful), and locking on it as Rack::Lock#call does is to be preferred if you just want to ensure no two instances of the same code is executed at the same time.

    Also, for the record, each request creates and dereferences one controller in each request cycle. No two requests should see the same instance, although they may see the same class.

    Setting config.threadsafe! voids almost everything I said. That removes Rack::Lock from the stack, and means you will need to set a mutex manually to prevent double entry. Don't do it unless you have a really good reason.

    Even without Rack::Lock you will still get one controller instance per request. The entry point to your controller ensures that, notice the call to new in process.

    0 讨论(0)
提交回复
热议问题