在Rails控制器中跨请求和跨用户保持内存价值?使用类变量?

时间:2012-03-15 18:43:40

标签: ruby-on-rails ruby-on-rails-3

我们正在使用Rails 3.0.6。

我们维护的数字列表每月只更改一次,但几乎每个页面请求都需要访问此列表。

我们将列表存储在数据库中。

我们不想在每个请求上访问数据库并抓住列表,而是希望获取一次数据并将其存储在内存中以便有效访问。

如果我们将列表存储在每个用户会话中,我们仍然需要为每个会话点击数据库。

有没有办法只打一次数据库,让值在所有用户和所有会话中保留在内存中?我们需要从控制器访问列表。我们应该在控制器中定义一个类变量吗?

谢谢!

2 个答案:

答案 0 :(得分:6)

我认为Rails.cache是您问题的答案。它是一个带有多个后端的简单界面,默认情况下将缓存存储在内存中,但是如果您已经在应用程序中使用了Memcached,Redis或类似设备,则可以将其插入到那些中。

尝试在ApplicationController中抛出与此类似的东西

def list_of_numbers
  @list_of_numbers ||= Rails.cache.fetch(:list_of_numbers, :expires_in => 24.hours) do
    # Read from database
  end
end

它会尝试从缓存中读取,但如果它找不到它,将会执行密集的操作并将其存储下次

答案 1 :(得分:2)

您正在寻找的模式被称为单例,这是一种缓存不随时间变化的东西的简单方法,例如,您经常会在application_controller.rb中看到类似的东西 - 您的代码总是调用方法

def current_user(user_id)
  @current_user ||= User.find user_id
end

当它执行时,它检查实例变量@current_user并返回它,如果不是nil,否则它执行数据库查找并将结果分配给它返回的实例变量。

您的问题类似,但更广泛,因为它适用于所有实例。

一个解决方案是使用类变量,此处记录http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_classes.html#S3 - 与上述类似的解决方案适用于此处。

这可能是一个很好的解决方案,但有一些问题。具体而言,(假设这是一个Web应用程序)取决于您的配置,您可能在不同进程中加载​​了多个Rails实例,并且类变量仅适用于其特定实例。流行的Passenger模块(适用于Apache和Nginx)可以配置为允许类变量可以被所有实例访问...如果你只有一个服务器,它就会很好用。

但是当你有多台服务器时,事情变得有点棘手。当然,您可以使用类变量并接受您必须为每个服务器对数据库进行一次命中。除了变量......变化的时候,这个效果很好!您需要某种方法在所有服务器上使变量无效。根据它的重要程度,这可能会产生各种非常难以追踪的错误(我学到了很多方法: - )。

输入memcached。这是一个很棒的工具,是一个通用的缓存工具。它非常轻巧,非常非常智能。特别是,它可以在服务器集群中创建分布式缓存 - 该值仅存储一次(因此避免了上面提到的同步问题),并且每个服务器都知道要查找哪个服务器以查找任何给定的缓存密钥。它甚至可以处理服务器出现故障以及各种其他不愉快的情况。

安装非常简单,Rails几乎假设你将它用于满足各种缓存需求,而Rails gem只是让它变得像馅饼一样简单。

假设存在其他机会来缓存可能不像您可以存储在类变量中的值那么简单的东西,那可能是第一个开始的地方。