我在网上阅读了大量有关不同版本的 Ruby 和 Rails 中的线程安全和性能的材料,我想我现在已经很好地理解了这些内容。
奇怪的是,讨论中似乎缺少的是如何实际部署异步 Rails 应用程序。当谈论应用程序中的线程和同步性时,人们想要优化两件事:
- 以最少的 RAM 使用量利用所有 CPU 核心
- 能够在之前的请求等待 IO 的同时处理新请求
第 1 点是人们对 JRuby 感到(正确地)兴奋的地方。对于这个问题我只是想优化第2点。
假设这是我的应用程序中唯一的控制器:
class TheController < ActionController::Base
def fast
render :text => "hello"
end
def slow
render :text => User.count.to_s
end
end
fast
没有 IO,每秒可以处理数百或数千个请求,并且slow
必须通过网络发送请求,等待工作完成,然后通过网络接收答案,因此比fast
.
因此,理想的部署将允许数百个请求fast
在请求时得到满足slow
正在等待 IO。
网络上的讨论似乎缺少的是堆栈的哪一层负责实现这种并发性。薄有一个--threaded
标志,它将“在线程中调用机架应用程序[实验]”——这是否会为每个传入请求启动一个新线程?在持续存在并等待传入请求的线程中缓冲机架应用程序实例?
瘦身是唯一的方法还是还有其他方法? ruby 运行时对于优化第 2 点重要吗?
适合您的方法在很大程度上取决于您的能力slow
方法正在做。
在完美的世界中,您可以使用类似的东西sinatra 同步 https://github.com/kyledrake/sinatra-synchronygem 处理 Fiber 中的每个请求。您只会受到最大光纤数量的限制。不幸的是,纤维上的堆叠尺寸是硬编码的 http://bugs.ruby-lang.org/issues/3187,并且在 Rails 应用程序中很容易出现溢出。此外,我读过一些关于调试光纤的困难的恐怖故事,因为异步 IO 启动后会自动让步。使用纤维时,竞争条件仍然是可能的。目前,纤维化 Ruby 有点贫民窟,至少在 Web 应用程序的前端是如此。
不需要更改代码的更实用的解决方案是使用具有工作线程池(例如 Rainbows)的机架服务器!或彪马。我相信薄的--threaded
flag 在新线程中处理每个请求,但启动本机操作系统线程并不便宜。最好使用池大小设置足够高的线程池。在 Rails 中,不要忘记设置config.threadsafe!
在生产中。
如果您同意更改代码,您可以查看 Konstantin Haase 的优秀文章在实时 Rack 上交谈 http://confreaks.net/videos/727-rockymtnruby2011-real-time-rack。他讨论了使用EventMachine::Deferrable
类在 Rack 构建的传统请求/响应周期之外生成响应。这看起来确实很简洁,但是您必须以异步风格重写代码。
还看一下Cramp http://cramp.in/ and Goliath http://postrank-labs.github.com/goliath/。这些可以让您实施您的slow
方法在与 Rails 应用程序一起托管的单独 Rack 应用程序中,但您可能需要重写代码才能在 Cramp/Goliath 处理程序中工作。
至于你关于 Ruby 运行时的问题,这也取决于slow
是在做。如果您正在进行 CPU 密集型计算,那么您将面临 GIL 出现问题的风险。如果你正在做 IO,那么 GIL不应该妨碍你。 (我说不应该是因为我相信我已经读过有关旧版 mysql gem 阻塞 GIL 的问题。)
就我个人而言,我已经成功地将 sinatra-synchrony 用于后端混搭 Web 服务。我可以并行向外部 Web 服务发出多个请求,并等待所有请求返回。同时,前端Rails服务器使用线程池,直接向后端发出请求。并不完美,但现在已经足够好了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)