PHP在无共享环境中运行,在这种情况下,这意味着每个Web请求都在一个干净的环境中运行.除了通过单独的持久层(文件系统,数据库等)之外,您无法访问其他请求的数据.
Ruby on Rails怎么样?我刚看了一篇博文,说明单独的请求可能会访问同一个类变量.
我想到这可能取决于Web服务器. Mongrel的FAQ说明Mongrel每个请求使用一个线程 - 建议无共享环境.FAQ继续说RoR不是线程安全的,这进一步表明RoR不会存在于共享环境中,除非新请求重用从先前请求创建的内存中对象.
显然这具有巨大的安全后果.所以我有两个问题:
RoR环境是否共享?
如果RoR在共享环境中运行(或者可能在某些情况下运行),那么我应该对哪些变量和其他数据存储感到偏执?
更新:我会进一步澄清. 在Java servlet容器中,您可以拥有跨多个请求持久化的对象.这通常用于缓存多个用户可以访问的数据,数据库连接等.在PHP中,这不能在应用程序层完成,它必须在像Memcached这样的单独的持久层中完成.因此,双重问题是:哪种情况是RoR(PHP或Java),如果像Java一样,哪些数据类型在多个请求中持续存在?
简而言之:
不,Rails 永远不会在无共享环境中运行.
对类变量和类实例变量要偏执.
版本较长:
Rails进程通过加载框架和应用程序来开始其生命周期.它们通常只运行一个线程,它将在其生命周期内处理许多请求.因此,将严格按顺序发送请求.
尽管如此,所有类仍然存在于请求中.这意味着将从您的类和元类引用的任何对象(例如类变量和类实例变量)将在请求之间共享.例如,如果您尝试在类方法中记住methods(),期望它只会在当前请求期间保持不变,这可能会让您感到困惑.实际上,计算仅在第一次请求时执行.@var ||= expensive_calculation
从表面上看,实现缓存或依赖于跨请求持久性的其他行为似乎很不错.通常,它不是.这是因为大多数部署策略将使用多个 Rails进程来抵消它们自己的单线程特性.在等待慢速数据库查询时阻塞所有请求根本不是很酷,因此最简单的方法是生成更多进程.当然,这些过程并不共享任何东西(除了一些内存,你可能不会注意到).如果您在请求期间保存类变量或类实例变量中的内容,这可能会让您感到困惑.然后,不知何故,有时候东西似乎存在,有时它似乎消失了.(实际上,当然,数据在某些过程中可能存在也可能不存在,而在其他过程中则不存在).
一些部署配置(最显着的JRuby + Glassfish的)是实际上多线程.Rails是线程安全的,所以它可以处理它.但您的应用程序可能不是线程安全的.每次请求后都会抛弃所有控制器实例,但正如我们所知,这些类是共享的.如果您在类变量或类实例变量中传递信息,这可能会让您感到困惑.如果你没有正确使用同步方法,你很可能最终处于竞争状态地狱.
作为旁注:Rails通常在单线程进程中运行,因为Ruby的线程实现很糟糕.幸运的是,Ruby 1.9中的情况要好一些.和很多 JRuby中更好.
随着这些Ruby实现越来越受欢迎,似乎多线程Rails部署策略也将获得普及和数量.最好在编写具有多线程请求调度的应用程序时考虑到这一点.
这是一个相对简单的示例,说明了如果您不小心修改共享对象会发生什么.
创建一个新的Rails项目: rails test
创建一个新文件lib/misc.rb
并输入:
class Misc @xxx = 'Hello' def Misc.contents() return @xxx end end
创建一个新的控制器: ruby script/generate controller Posts index
更改app/views/posts/index.html.erb
以包含此代码:
<% require 'misc'; y = Misc.contents() ; y << ' (goodbye) ' %><%= y %>
(这是我们修改隐式共享对象的地方.)
添加RESTful路由到config/routes.rb
.
启动服务器ruby script/server
并/posts
多次加载页面.您将看到( goodbye)
每个连续页面重新加载时字符串的数量增加1.