$redis 全局变量与 ruby​​ on rails

Posted

技术标签:

【中文标题】$redis 全局变量与 ruby​​ on rails【英文标题】:$redis global variable with ruby on rails 【发布时间】:2014-01-31 06:48:11 【问题描述】:

我使用 redis 作为读取缓存。我创建了一个初始化器

config/initializer/redis.rb

$redis = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

我在我的 unicorn.rb 中使用这个全局变量来创建一个新的连接,无论何时创建一个新的 worker。

before_fork do |server, worker|
  # clear redis connection
  $redis.quit unless $redis.blank?
end

# Give each child process its own Redis connection
after_fork do |server, worker|
  $redis = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])
end

每当我需要访问我的 redis 服务器时,我也会使用这个全局变量。但是我不习惯使用这个全局变量。有没有比使用全局变量更好的选择?

【问题讨论】:

***.com/a/16474679/19079 比以下任何答案都回答得更好。 【参考方案1】:

Redis.current,您可以使用它来存储您的唯一Redis 实例。

因此,您可以按如下方式分配您的实例,而不是使用$redis

Redis.current = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

Redis.current 是 introduced to redis-rb in 2010 作为获取 redis 连接的标准方式,所以我很惊讶没有其他答案提到它。

更新:从 4.6.0 版开始Redis.currenthas been deprecated。作者指出,典型的多线程应用程序会在共享 redis 客户端周围发现很多锁定。他们建议定义一个自己的地方来获取redis客户端,但也建议使用连接池。

因此,公认的答案是实现与Redis.current 相当的目标的最简单解决方案,但在多线程环境中可能表现不佳。

【讨论】:

令人讨厌的是,这是我一直在寻找的答案,但因为它低于公认的答案,所以我浪费时间不这样做。 恕我直言,这应该是公认的答案。 现在已弃用。惊喜。多么有趣。 我提交了一个问题,要求提供“官方”建议:github.com/redis/redis-rb/issues/1064 @courtsimas 感谢您注意到这一点。我已经在相应的提交中询问并相应地更新了答案。【参考方案2】:

进一步扩展 mestachs 建议,在初始化程序中命名模块,如下所示

config/initializers/redis.rb

module ReadCache
  class << self
    def redis
      @redis ||= Redis.new(:url => (ENV["REDIS_URL"] || 'redis://127.0.0.1:6379'))
    end
  end
end

然后在 unicorn.rb 中

 before_fork do |server, worker|
    ...
   if defined?(ReadCache.redis)
    ReadCache.redis.quit
   end
    ...
 end

 after_fork do |server, worker|
    ...
   if defined?(ReadCache.redis)
    ReadCache.redis.client.reconnect
   end
    ...
 end

【讨论】:

那么,如果我同时有上千个独特的请求,我还需要上千个连接到 redis 服务器,对吧? @Kira 你能解释一下吗。我认为它不需要数千个连接。如果我理解正确,这个模块将是整个应用程序通用的。 这篇文章devcenter.heroku.com/articles/… 有助于了解连接池天气或不使用 Heroku。稍微了解一下,您可以让他们调整您的 sidekiq 并发性,请参阅:github.com/mperham/sidekiq/wiki/…。 @blootto 读得很好,谢谢。虽然,您的示例代码没有使用 ActiveRecord(如果我错了,请纠正我),所以它没有使用池化技术,但我们仍然有每个客户端/工作人员的 redis 连接。 使用Redis.current,如下@NobodysNightmare 所述。【参考方案3】:

如果你还没有使用另一个 Rails.cache,我建议你只使用 redis 的机制。

gem redis-store 让这变得非常简单 (https://github.com/redis-store/redis-store)

这样你就可以做到Rails.cache.reconnect,一切都很好

https://github.com/redis-store/redis-store/issues/21#issuecomment-948569

它还允许您使用很棒的 Rails.cache API,它有一些简洁的功能:http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html

【讨论】:

【参考方案4】:

一个更命名空间选项来替换你的全局变量,你可以在一个模块中创建一个方法

模块缓存 def self.redis ...在这里初始化/记忆/重新连接... 结尾 结尾

然后你调用它:

Caching.redis

【讨论】:

【参考方案5】:

试试这个:-

您可以使用constant 代替global variable.like in config/initializer/redis.rb

REDIS = Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])

在 unicorn.rb 中

before_fork do |server, worker|
  # clear redis connection
  REDIS.quit if defined?(REDIS)
end

#给每个子进程自己的Redis连接

after_fork do |server, worker|
  REDIS ||= Redis.new(:host => ENV["REDIS_HOST"], :port => ENV["REDIS_PORT"])
end

【讨论】:

听起来很有趣,这个常量的作用是什么?我的意思是,它不是全局变量,但它的行为类似于全局变量。【参考方案6】:

根据this Heroku,您不需要将$redis 添加到您的独角兽:

将 Redis Cloud 与 Unicorn 服务器一起使用时,无需进行特殊设置。在 Unicorn 上运行 Rails 应用程序的用户应遵循Configuring Redis from Rails 部分中的说明,用户...

这是 Rails 4 之前的所有“Configuring Redis from Rails 部分”(除了 Gemfile 和其他一些之前 Rails 3 的东西):

# config/initalizers/redis.rb

if ENV["REDISCLOUD_URL"]
  uri = URI.parse(ENV["REDISCLOUD_URL"])
  $redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
end

它并没有真正解释为什么“不需要特殊设置”。

【讨论】:

我知道您确实需要做一些特别的事情。我已经使用 unicorn 在生产 heroku 服务器上连接了 512 个 redis 云实例。

以上是关于$redis 全局变量与 ruby​​ on rails的主要内容,如果未能解决你的问题,请参考以下文章

ruby on rials (项目实战技巧)

ruby on rails 的redis 使用

如何在 Ruby-on-Rails 中生成 PDF 表单

如何在 Windows 操作系统的 Ruby on Rails 应用程序中使用 Redis?

雷林鹏分享:Ruby 变量

如何将 Juggernaut 聊天服务器(由 ruby​​ on rails 提供支持)与 iPhone/Android 应用程序集成以进行实时聊天?