【问题标题】:Redis::TimeoutError while using redis-rails on heroku在 Heroku 上使用 redis-rails 时出现 Redis::TimeoutError
【发布时间】:2013-05-30 03:38:55
【问题描述】:

更新包括 Redis/Resque 版本和堆​​栈跟踪(下):

redis (3.0.4)
redis-namespace (1.3.0)
  redis (~> 3.0.0)
redis-store (1.1.2)
  redis (>= 2.2.0)
resque (1.24.1)
  mono_logger (~> 1.0)
  multi_json (~> 1.0)
  redis-namespace (~> 1.2)
  sinatra (>= 0.9.2)
  vegas (~> 0.1.2)
resque-scheduler (2.0.1)
  redis (>= 2.0.1)
  resque (>= 1.20.0)
  rufus-scheduler

我在使用 Rails.cache.fetch 命令将中等大小的数组(~200 Fixnums)写入 Redis 存储时,在 heroku 上看到间歇性的 Redis::TimeoutError: Connection timed out

我也在使用 Resque。

我看到here Redis::Client 可以接收超时选项,但我看不到将初始化选项传递给 Redis 的位置。

我正在使用标准的 heroku resque.rb:

rails_root        = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env         = ENV['RAILS_ENV'] || 'development'

resque_config     = YAML.load_file(rails_root + '/config/resque.yml')
ENV['REDIS_URI']  = resque_config[rails_env]
Resque.redis      = resque_config[rails_env]
Resque.inline     = rails_env == 'test'

require 'resque_scheduler'
require 'resque/scheduler'
require 'resque_scheduler/server'

Resque.schedule   = YAML.load_file(rails_root + '/config/resque-schedule.yml')

Resque.before_fork do
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

Resque.after_fork do
  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

我假设这里实例化了一个 Redis 客户端。这是与production.rb 中实例化的客户端不同的客户端吗:

rails_root          = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env           = ENV['RAILS_ENV'] || 'development'
resque_config = YAML.load_file(rails_root + '/config/resque.yml')
config.cache_store = :redis_store, resque_config[rails_env], { expires_in: 14.days }

据我所知,Rails.cache 选项的选项哈希。是否在此处实例化了新客户端?如何将选项传递给这个?


已更新以在 heroku 控制台中包含此实验,这意味着它们是不同的客户端实例:

irb(main):002:0> Rails.cache
=> #<ActiveSupport::Cache::RedisStore:0x00000003860e18 @data=#<Redis client v3.0.4 for redis://spinyfin.redistogo.com:9485/0>, @options={:expires_in=>14 days}>
irb(main):003:0> Resque.redis.redis
=> #<Redis client v3.0.4 for redis://spinyfin.redistogo.com:9485/0>
irb(main):004:0> Rails.cache.instance_variable_get(:@data).object_id == Resque.redis.redis.object_id
=> false

堆栈跟踪:

Redis::TimeoutError: Connection timed out
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:208:in `rescue in io'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:206:in `io'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:214:in `read'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:84:in `block in call'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:187:in `block (2 levels) in process'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:295:in `ensure_connected'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:177:in `block in process'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:256:in `logging'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:176:in `process'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis/client.rb:84:in `call'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis.rb:644:in `block in setex'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis.rb:36:in `block in synchronize'
    from /app/vendor/ruby-1.9.3/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis.rb:36:in `synchronize'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-3.0.4/lib/redis.rb:643:in `setex'
    from /app/vendor/bundle/ruby/1.9.1/gems/redis-store-1.1.2/lib/redis/store/interface.rb:17:in `setex'
... 11 levels...
    from /app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/cache.rb:299:in `fetch'
    ...SNIP...
    ...my code...
    ...SNIP...
    from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/relation/delegation.rb:6:in `each'
    from /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/relation/delegation.rb:6:in `each'
    ...SNIP...
    ...my code...
    ...SNIP...
    from (irb):5
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/console.rb:47:in `start'
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/console.rb:8:in `start'
    from /app/vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'irb(main):006:0>  !    Heroku client internal error.
 !    Search for help at: https://help.heroku.com
 !    Or report a bug at: https://github.com/heroku/heroku/issues/new

    Error:       Operation timed out (Errno::ETIMEDOUT)
    Backtrace:   /usr/local/heroku/ruby/lib/ruby/1.9.1/openssl/buffering.rb:121:in `sysread'
                 /usr/local/heroku/ruby/lib/ruby/1.9.1/openssl/buffering.rb:121:in `readpartial'
                 /Users/me/.heroku/client/lib/heroku/client/rendezvous.rb:69:in `block in start'
                 /Users/me/.heroku/client/lib/heroku/client/rendezvous.rb:53:in `loop'
                 /Users/me/.heroku/client/lib/heroku/client/rendezvous.rb:53:in `start'
                 /Users/me/.heroku/client/lib/heroku/command/run.rb:132:in `rendezvous_session'
                 /Users/me/.heroku/client/lib/heroku/command/run.rb:119:in `run_attached'
                 /Users/me/.heroku/client/lib/heroku/command/run.rb:24:in `index'
                 /Users/me/.heroku/client/lib/heroku/command.rb:206:in `run'
                 /Users/me/.heroku/client/lib/heroku/cli.rb:28:in `start'
                 /usr/local/heroku/bin/heroku:24:in `<main>'

    Command:     heroku run rails c
    Plugins:     heroku-redis-cli
    Version:     heroku-toolbelt/2.39.4 (x86_64-darwin10.8.0) ruby/1.9.3

【问题讨论】:

  • 您能告诉我们您使用的是哪个版本的 redis-store 吗?较新的 gem 的超时配置是不同的。查看您收到 TimeoutError 的位置的跟踪也会很有趣 - 特别是连接到 Resque 时,还是连接到您用于缓存的 Redis 连接时?
  • @BrianPO'Rourke 更新了几条新数据。

标签: ruby-on-rails heroku redis resque redistogo


【解决方案1】:

“连接超时”消息表示redis-rb 在启动与您的 Redis 服务器的连接时遇到问题。通常,您只想在应用启动时初始化您的 Redis 连接。然而,由于 Resque 分叉(而不是使用像 Sidekiq 这样的线程),它必须为每个单独的作业初始化一个新的 Redis 连接。

通常,这不是问题,但 Heroku 在创建新的 Redis 连接时会出现间歇性问题。我已经在语言/客户端库/Redis 主机中看到了这个问题,除了减少您创建的 Redis 连接数量之外,缓解该问题的唯一方法是在您的 Resque.after_fork 块中自动重试连接到 Redis 几次。 (例如,如果您尝试了 3 次,则捕获超时错误并重试或重新引发异常)

【讨论】:

  • 这意味着 Resque 和 redis_store 共享一个客户端实例?看看我上面更新的控制台实验。
  • 澄清一下,错误发生在尝试Rails.cache.fetch 时,而不是在 Resque 作业期间。
【解决方案2】:

这不是一个很好的答案,但是在同事的推荐下,我从 RedisToGo 切换到了 openredis,这些问题立即消失了。

【讨论】:

  • 这仍然发生在我的 openredis 上
【解决方案3】:

由于this bug,您不能同时将 URL 和选项哈希传递给 config.cache_store

config.cache_store = :redis_store, resque_config[rails_env], { expires_in: 14.days }

如果您传递 URL 和选项哈希,它会忽略 URL 并默认为 localhost,这可能就是您看到超时的原因(我刚刚遇到了同样的问题)。

相反,请执行以下操作:

redis_uri = URI.parse(ENV["REDISTOGO_URL"])
config.cache_store = :redis_store, {
  host: redis_uri.host,
  port: redis_uri.port,
  password: redis_uri.password,
  namespace: "cache",
  expires_in: 7.days
}

【讨论】:

  • 我不这么认为...请参阅上面的控制台输出,这表明远程 URL 已正确设置。
猜你喜欢
  • 1970-01-01
  • 2015-06-07
  • 2017-06-28
  • 2020-09-13
  • 2021-05-17
  • 2014-10-16
  • 2020-01-19
  • 2013-10-14
  • 1970-01-01
相关资源
最近更新 更多