Rails 在 action 里调用一个耗时很长的外部命令时,整个应用都被卡死的问题

lululau · February 19, 2013 · Last by lululau replied at February 19, 2013 · 2993 hits
class PeopleController < ApplicationController
  # GET /people
  # GET /people.json
  def index
    @people = Person.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render :json => @people }
    end
  end

  # GET /people/1
  # GET /people/1.json
  def show
    @person = Person.find(params[:id])
    system "sleep 1000000"
    respond_to do |format|
      format.html # show.html.erb
      format.json { render :json => @person }
    end
  end

  ....

end

如上面的代码所示,名为 show 的 action 里,调用了"sleep 1000000",然后整个应用全部卡死了,不管通过浏览器访问哪个 action 都不返回。

请教这个问题怎么解决?

Background job in queue, use delayed_job, sidekiq etc.

system "sleep 1000000 &"

是这样的,我需要这个命令的返回,show 这个方法应该是这样的:

@ashchan @hooopo

def show
  @person = Person.find(params[:id])
  @person.xxxx = %x{sleep 1000000}
  respond_to do |format|
    format.html # show.html.erb
    format.json { render :json => @person }
  end
end

可不可以显示一个处理中的页面,处理完成了才渲染结果页面。

要避免应用卡死还是要用异步处理。

这个如果在线上的话 html 超时自己就会把连接中断了吧 线上应用必然不能这样处理吧 @lululau 改改业务逻辑呢

能否用 The Worker Pattern 的方案。

请求来了之后,直接返回,后台处理。然后让客户端轮询,直到处理完毕,返回结果。

参考 https://gist.github.com/ryandotsmith/1660752

我弄明白了,Rails 默认是单线程的,我的应用非常简单,所以也不用考虑什么线程安全问题,所以我简单的在 config/environments/${RAILS_ENV}.rb 里加上 config.threadsafe! 就行了。不过好像如果用 Mongrel/Passenger 的话,这样修改也是不行的,各位有兴趣的话可以看下这篇文章:

http://jordanhollinger.com/2011/04/23/how-to-deploy-a-multi-threaded-rails-app

感谢各位的热情帮助~

#8 楼 @lululau 多线程并不能解决你的问题。线程数总是有限的,你那个“耗时很长的外部命令”的 action 不走异步的话,在任何环境下都会影响并发数。

#9 楼 @ashchan 你说的没错,但是我这个应用足够简单,这个“耗时很长的外部命令”的 action 在同一时间只会运行一个,其他操作都不耗时,也不写入数据,因此这样修改对我现在这个情况来说最简单

You need to Sign in before reply, if you don't have an account, please Sign up first.