【问题标题】:In Ruby on Rails how can I persist objects in memory between sessions在 Ruby on Rails 中,如何在会话之间将对象持久保存在内存中
【发布时间】:2010-07-28 14:58:08
【问题描述】:

我正在尝试构建如下工作的东西(最终是一个 gem,但现在是一个应用程序)。

假设例如数据库记录是狗的品种。每个品种都有一个 Dog 父类和一个子类。实际品种直到运行时才知道。

当服务器启动时,它将从数据库加载记录并根据记录实例化类的实例,例如我可能有两只小猎犬和贵宾犬。当有人访问服务器时,他们可能想要访问其中一个狗实例。

为什么不直接创建实例呢?就我而言,“狗”基本上是包含算法和数据的类。算法不会改变,数据很少改变(以天为单位),但算法本身的执行,它使用数据和一些传入的动态数据(如时间戳),每秒将被访问多次。

每次都必须重新创建对象的实例并加载数据只是为了执行一个请求而在下一个请求时再次执行它是愚蠢的(请求不会更改对象的状态)。当我可以重用同一个对象时,我会在一秒钟内创建和销毁多个对象。

将它保留在会话中是没有意义的,因为想要贵宾犬的人不应该需要在她的会话对象中包含小猎犬信息;这是无关紧要的(并且无法扩展)。

如何将这些对象保存在内存中?我基本上想要一个查找表来保存实例。在 Java 中,我会创建一个带有某种类型的哈希图或位于内存中的数组的单例。在rails中,我通过在lib文件夹中创建一个单例类来尝试这个。我认为——我可能不理解这一点——当会话消失时,实例(它是单例的事实是没有实际意义的)正在丢失。

我找到的最接近的答案是http://www.ruby-forum.com/topic/129372,它基本上将所有内容都放在类字段和方法中。不知怎的,这似乎不对。

TIA!

补充:我来自Java。在 Java 中,我只需创建一个位于堆上或可能位于 JNDI 树中的对象,当 HTTP 请求进入时,它们将由一个 servlet 或 EJB 或一些可以访问持久性对象的每个请求项处理。我似乎无法在 rails 中找到等价物。

【问题讨论】:

  • +1 问题,我有一棵树用于查找。它根本不会改变,我不想在每个请求中都构造它。

标签: ruby-on-rails memory object persistence


【解决方案1】:

也许您的示例因其简单性而令人困惑。我假设您的对象非常复杂,并且您的基准测试表明在每个请求上都构建它们是不合理的。

在生产模式下,类不会在请求之间卸载,但这些类的实例会。因此,使用班级的班级成员听起来像是要走的路。只需使用它来存储您的实例。

class ObjectCache
  @@objects = {:beagle => Beagle.new, :poodle => Poodle.new}

  def lookup key
    @@objects[key.to_sym]
  end
end

【讨论】:

  • 感谢您的回复。您提到“在生产模式下”。我在开发模式下尝试这个,我仍然在请求之间丢失了对象(即使方法也是类方法,例如 def self.lookup 键)但也许这是因为模式。我现在会接受这种工作,但坦率地说,这真的感觉是错误的做法。
  • 当您在请求之间保留 ActiveRecord 模型的实例时会发生什么并没有很好地定义,因此最好避免这种情况。如果你只存储普通的 Ruby 对象,它可能会起作用。
  • 好警告;幸运的是,这些只是 ruby​​ 对象,而不是 ActiveRecord 对象,所以我不担心在数据库内外混淆状态。我在上面给出的示例中的对象“狗”的状态来自从数据库和算法加载(读取,从不写入)的一些数据。没有对象与数据库不一致的风险,因为在这种情况下数据库中没有对象。
  • 您也可以防止类在开发模式下被卸载。
  • 怎么样? apidock.com/rails/Module/unloadable 表示 unloadable 已折旧。我发现最好的是strd6.com/2009/04/cant-dup-nilclass-maybe-try-unloadable 这是正确的做法吗? (Unloadable 看起来干净多了。)
【解决方案2】:

我不会太担心加载和丢弃对象,除非您能提出一个基准来证明这是一个问题。当然,每个请求都会创建大量的中间对象,而这些中间对象通常会在几毫秒内创建和销毁。

通常最好只专注于加载所需的内容,对数据库进行反规范化以将经常访问的数据或方法推送到方便的位置,或者将复杂计算的结果保存在缓存字段中。

先进行基准测试,仅在需要时进行优化。

将模型的实例保存到类缓存中是可行的,但这只是生产环境中的一种选择,在这种环境中模型类不会随每个请求重新加载。它还可能使您面临由过时数据引起的错误。

如果您遇到无法使用这些方法解决的扩展问题,您可能需要研究使用 Rack 和 EventMachine 的组合为您的部分功能构建持久服务器。有许多方法可以构建一个可以使用预加载的数据集执行复杂计算的后台进程,但具体的方法将取决于几件事,例如您正在使用的数据类型和频率它将被访问。

【讨论】:

  • 感谢您抽出宝贵时间回复。撇开缩放不谈,这似乎不是正确的编程。从 OO 的角度来看,当对象本身可以被重用时,不断地重新创建同一个对象是错误的。 Rack 似乎使用了更轻量级的 Rails 版本,我在文档中没有看到任何表明它可以实现 Rails 无法做到的持久性。 EventMachine 似乎是正确的答案。 neeraj.name/2009/12/15/… 对此有很好的解释。我要深入研究一下。
  • HTTP 的无状态特性通常会导致这样的问题,即一个请求可能与下一个请求无关,并且诸如 Rails 之类的应用程序框架通常必须清理在事先请求为下一个腾出空间。如果你需要一个持久的环境,构建一个在后台运行的引擎,你会发现不仅可以避免冗余重新加载,还可以从异步操作中获得巨大的性能提升。编写一个“说” Memcache 协议的服务器非常简单,即使它计算答案,它也可以像普通缓存一样使用。
  • 我说得太早了。 EventMachine 仅在连接中持续存在。我将无法在 Web 请求之间保持连接,因此我仍然会丢失状态。还有其他构建引擎的方法吗?
  • 我说的是构建一个 EventMachine 引擎,它作为一个独立于 Rails 应用程序的进程运行,而不是在 Rails 环境中构建 EventMachine 模块。您在这里谈论加载多少个对象?大多数现代硬件可以每秒导入和实例化 100K,而无需进行太多优化。
  • 对象不多,早期可能可以扩展。它更具哲学意义,因为有些事情感觉不对。如果您查看我在原始帖子底部所做的编辑,您会看到我是如何在多个场合用 Java 完成的。我本可以重新创建对象(在某些但不是所有情况下),但 Java 让我将对象保存在堆上的内存中(跨任何和所有会话)。 Rails 不能做到这一点,缺少一个有用的功能,这感觉是错误的。
【解决方案3】:

在生产中,控制器和模型类不会在请求之间重新加载,因此您有多种选择:

  • 在 application_controller 中将对象设置为类变量
  • 在模型类本身中创建返回值的单例方法

【讨论】:

    【解决方案4】:

    是的,在开发模式下也可以防止类卸载。这不仅仅是生产模式的事情。虽然它默认发生在生产模式下,但您必须在开发模式下手动设置它。

    【讨论】:

      【解决方案5】:

      为了即使在开发模式下也不会在每个请求上重新加载您的类,您可以将它们移动到自动加载路径之外的某个位置(如果在应用程序和 lib 目录之外使用默认值)。这样,您可以让这些对象类在请求之间保持不变,从而使用它们来存储每个请求都相同的数据。

      【讨论】:

        猜你喜欢
        • 2013-11-08
        • 1970-01-01
        • 2013-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-01
        • 2011-10-04
        • 1970-01-01
        相关资源
        最近更新 更多