【问题标题】:Ruby on Rails loopback requests cause deadlock?Ruby on Rails 环回请求导致死锁?
【发布时间】:2013-01-15 20:30:07
【问题描述】:

解决方案

我正在开发模式下切换到独角兽。每一级递归都需要一个工作进程来防止死锁情况,所以我使用了 2 个工作进程。

问题

我正在开发环境中的Thin 服务器上工作。我使用端口 3000(开发环境中的默认端口)。我的问题是让服务器向自己发出请求。

假设我有以下控制器:

# app/controllers/recursions_controller.rb
class RecursionsController < ApplicationController

    # /recursions
    def index

        # synchronously call recursions#show
        RestClient.get("http://localhost:3000/recursions/1") 

        # finish!
        render :text => 'index'

    end

    # /recursions/:id
    def show

        # finish immediately
        render :text => 'show'

    end

end

这里是对应的路线:

# config/routes.rb
resources :recursions

这是我最初请求recursions#index时请求日志的输出:

[INFO] 2013-01-15 12:09:05 -0800 Started GET "/recursions" for 127.0.0.1 at 2013-01-15 12:09:05 -0800
Processing by RecursionsController#index as HTML
Completed 500 Internal Server Error in 60049ms

[FATAL] 2013-01-15 12:10:05 -0800 RestClient::RequestTimeout (Request Timeout):
  app/controllers/recursions_controller.rb:8:in `index'
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_trace.erb (0.8ms)
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.7ms)
  Rendered /usr/local/lib/ruby/gems/1.9.1/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (7.4ms)

[INFO] 2013-01-15 12:10:05 -0800 

[INFO] 2013-01-15 12:10:05 -0800 

[INFO] 2013-01-15 12:10:05 -0800 Started GET "/recursions/1" for 127.0.0.1 at 2013-01-15 12:10:05 -0800
Processing by RecursionsController#show as XML
  Parameters: {"id"=>"1"}
  Rendered text template (0.0ms)
Completed 200 OK in 7ms (Views: 5.5ms)

我怀疑这里发生的情况是某种僵局。在请求 B 返回之前,请求 A 无法返回(递归排序,无能为力),但在请求 A 返回之前无法处理请求 B(我的网络服务器中内置了明显的限制?)。当 RestClient 超时时,死锁被解决,导致异常并以 500 终止请求 A。只有这样才处理请求 B,尽管此时它没有实际意义。

在我看来,我的网络服务器无法处理并发请求。也就是说,这是我的问题:

  1. 在我的开发环境中,我是否可以切换到不受这种限制的网络服务器?我们在生产环境中使用 Unicorn,它可以产生多个工作进程并因此处理并发请求,但 Unicorn 对于开发环境来说似乎太重了。使 Unicorn 成为我的问题的解决方案的同一件事可能会使读取日志输出变得困难。这是我最后的解决方案。

  2. 是否有一种巧妙的方法可以绕过明显的并发请求限制向 Rails/Rack 框架发出请求?

  3. 谁能向我提供明确说明此限制的文档?我不知道这是所有单进程 Ruby on Rails 网络服务器固有的限制,还是只是 Thin。

注意:这只是一个玩具问题,用于演示我遇到的阻塞问题。如果你想知道这个的真正原因,那是我正在将一些 HTTP 服务从我们基础设施的另一部分转移到我们的 RoR 应用程序中。我们的 RoR 应用程序在代码中的许多不同点使用这些服务,所以我试图让这些服务的客户端代码保持不变,只更改实现。这意味着发出循环 HTTP 请求。这将在以后一切稳定后进行优化。

【问题讨论】:

  • 该解决方案实际上调试复杂......工作进程的输出在日志中交错。

标签: ruby-on-rails thin unicorn


【解决方案1】:

您是正确的,默认情况下您的应用一次只会处理一个请求。这个限制是由Rack::Lock 中间件实现的——无论你使用什么网络服务器,它都会存在。

您可以通过在相应的 environment.rb 文件中调用 config.thread_safe! 来更改此设置。然而,rails 的开发模式代码重新加载不是线程安全的,并且被此设置禁用,这使得它在开发中无法真正使用。究竟是什么config.thread_safe!确实在rails configuration guide 中进行了描述。

passenger、pow、unicorn 都可以轻松运行多个实例。

【讨论】:

    【解决方案2】:

    我的一位客户遇到了这个确切的问题。正如您所描述的那样 - 由于您正在运行一个只有一个进程的单线程网络服务器,因此您一次只能使用 1 个客户端。如果您发出第二个请求或环回请求,操作系统会将您的第二个请求排入队列,直到第一个超时。

    切换到独角兽并不能从本质上解决问题,但在我们的例子中,我们使用的是独角兽,我们只是将工人数增加到“2”。我对瘦不够熟悉,无法推荐如何增加工人数量,但毫无疑问有办法。否则,请使用独角兽 - 如果您进行切换,您还将获得更好的开发/产品平价,所以这可能是值得的。

    【讨论】:

    • 很高兴我不是唯一遇到此问题的人。在 dev 中运行 Unicorn 是否有任何问题?性能问题或可能难以与多个工作人员一起调试?
    • 调试多个工人绝对是一个挑战。从那以后,我们不再对自己进行 HTTP 调用,主要是因为这个原因。调试起来更困难,在我们的例子中,它并不是真正需要的。独角兽的表现相当不错——这不是我们的限制因素。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多