23

PHP 在无共享的环境中运行,在这种情况下,这意味着每个 Web 请求都在干净的环境中运行。除非通过单独的持久层(文件系统、数据库等),否则您无法访问其他请求的数据。

Ruby on Rails 怎么样?我刚刚阅读了一篇博文,指出单独的请求可能会访问同一个类变量。

我突然想到这可能取决于 Web 服务器。 Mongrel 的常见问题解答指出,Mongrel 每个请求使用一个线程 - 建议使用无共享环境。FAQ 继续说 RoR 不是线程安全的,这进一步表明 RoR 不会存在于共享环境中,除非新请求重用从先前请求创建的内存中对象。

显然,这具有巨大的安全影响。所以我有两个问题:

  1. RoR 环境是无共享的吗?
  2. 如果 RoR 在共享环境中运行(或可能在某些情况下运行),我应该对哪些变量和其他数据存储感到偏执?

更新:我会进一步澄清。 在 Java servlet 容器中,您可以拥有跨多个请求持续存在的对象。这通常用于缓存多个用户可以访问的数据、数据库连接等。在 PHP 中,这不能在应用程序层完成,它必须在像 Memcached 这样的单独持久层中完成。因此,双重问题是:哪种场景是 RoR(PHP 或 Java),如果是 Java,哪些数据类型在多个请求中持续存在?

4

4 回答 4

35

简而言之:

  1. 不,Rails永远不会在无共享环境中运行。
  2. 对类变量类实例变量保持偏执。

更长的版本:

Rails 进程通过加载框架和应用程序开始其生命周期。它们通常只运行一个线程,该线程将在其生命周期内处理许多请求。因此,请求将严格按顺序发送。

尽管如此,所有类在请求中仍然存在。这意味着从您的类和元类(例如类变量和类实例变量)引用的任何对象都将在请求之间共享。这可能会咬你,例如,如果你试图在你的@var ||= expensive_calculation方法中记忆方法( ),期望它只会在当前请求期间持续存在。实际上,只会对第一个请求执行计算。

从表面上看,实现缓存或其他依赖于跨请求持久性的行为似乎很好。通常情况下,它不是。这是因为大多数部署策略将使用多个Rails 进程来对抗它们自己的单线程特性。在等待缓慢的数据库查询时阻止所有请求根本不酷,因此简单的方法是生成更多进程。自然,这些进程不共享任何东西(也许除了一些你不会注意到的内存)。如果您在请求期间将内容保存在类变量或类实例变量中,这可能会咬到您。然后,不知何故,有时这些东西似乎存在,有时似乎消失了。(当然,在现实中,数据可能存在也可能不存在于某些process,而在其他人中不存在)。

一些部署配置(最著名的是 JRuby + Glassfish)实际上是多线程的。Rails 是线程安全的,所以它可以处理它。但是您的应用程序可能不是线程安全的。所有控制器实例在每次请求后都会被丢弃,但正如我们所知,这些类是共享的。如果您在类变量或类实例变量中传递信息,这可能会咬到您。如果您没有正确使用同步方法,您很可能会陷入竞争状态地狱。


附带说明:Rails 通常在单线程进程中运行,因为 Ruby 的线程实现并不完美。幸运的是,Ruby 1.9 的情况要好一些。在JRuby 中要好得多。

随着这两种 Ruby 实现越来越流行,多线程 Rails 部署策略似乎也将越来越流行和数量。在编写应用程序时考虑到多线程请求分派是一个好主意。

于 2009-06-22T22:51:50.743 回答
6

这是一个相对简单的示例,说明如果您不小心修改共享对象会发生什么。

  1. 创建一个新的 Rails 项目:rails test

  2. 创建一个新文件lib/misc.rb并将其放入:

    class Misc
      @xxx = 'Hello'
      def Misc.contents()
        return @xxx
      end
    end
    
  3. 创建一个新的控制器:ruby script/generate controller Posts index
  4. 更改app/views/posts/index.html.erb以包含此代码:

    <%
      require 'misc'; y = Misc.contents() ; y << ' (goodbye) '
    %>
    <pre><%= y %></pre>
    

    (这是我们修改隐式共享对象的地方。)

  5. 将 RESTful 路由添加到config/routes.rb.
  6. 启动服务器并多次ruby script/server加载页面。/posts您将看到( goodbye)每次连续页面重新加载时字符串的数量都会增加一个。
于 2010-02-08T18:08:00.363 回答
3

在使用Passenger 的平均部署中,您可能有多个应用程序进程,它们之间不共享任何内容,但每个进程中的类在请求之间保持其(静态)状态。但是,每个请求都会创建一个新的控制器实例。

您可以将其称为一组不同的共享状态环境。

用你的 Java 类比,你可以做缓存并让它在请求之间工作,你不能假设它在每个请求上都可用。

于 2009-06-22T23:22:38.653 回答
2

Shared-nothing 有时是个好主意。但是,当您必须为每个请求加载大型应用程序框架和大型域模型以及大量配置时,情况并非如此。

为了提高效率,Rails 会在内存中保留一些可用数据,以便在应用程序的整个生命周期内在所有请求之间共享。这些数据大部分是只读的,因此您不必担心。

当你编写你的应用程序时,远离写入共享对象(例如,不包括数据库,它是开箱即用的,具有良好的并发控制),你应该没问题。

于 2009-06-22T04:35:19.647 回答