【问题标题】:Reconnect with Redis after Puma forkPuma fork 后重新连接 Redis
【发布时间】:2013-09-14 06:25:42
【问题描述】:

我在 Rails 应用程序中使用全局变量来存储使用 redis gem 的 redis 客户端。在config/initializers/redis.rb,我有

$redis = Redis.new(host: "localhost", port: 6379)

然后在应用程序代码中,我使用$redis 处理 Redis 存储中的数据。

我还在生产环境中使用puma 作为Web 服务器,并使用capistrano 来部署代码。在部署过程中,capistrano 重启 puma。

每次启动或重新启动 puma Web 服务器时,当我第一次使用 $redis 访问 Redis 存储中的数据时,总是会收到“内部服务器错误”。我看到像Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)这样的错误

用 google 和 stackoverflow 搜索让我认为在 puma fork 子进程之后我需要重新连接到 Redis。所以,我在我的config/puma.rb 中添加了:

on_worker_boot do
  $redis.ping
end

但我仍然收到由Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.) 引起的“内部服务器错误”。

我看到了这个帖子http://qiita.com/yaotti/items/18433802bf1720fc0c53。然后我尝试添加config/puma.rb:

on_restart do
  $redis.quit
end

那没用。

我在Redis.new 之后尝试了config/initializers/redis.rb$redis.ping。那也没用。

如果 puma 在没有运行 puma 进程的情况下启动,或者在运行 puma 进程的实例时重新启动,我会收到此错误。

刷新页面会让我摆脱这个错误。但即使是第一次尝试使用$redis,我也想摆脱这种情况。我在想我没有使用redis gem 或正确配置它的重新连接。谁能告诉我:

  1. 这是在 Rails 应用程序中使用 redis gem 的正确方法吗?
  2. redis 连接应该如何在puma 中重新连接?

puma gem 文档说,“你应该在这个块中放置关闭全局日志文件、redis 连接等的代码,这样它们的文件描述符就不会泄漏到重新启动的进程中。不这样做会导致缓慢由于服务器多次重新启动,描述符耗尽并最终导致崩溃。”它在谈论on_restart 块。但它没有说明应该如何做。

【问题讨论】:

  • 我遇到了同样的错误,无法正常工作。到目前为止你能找到任何答案吗?
  • 不,对我来说这仍然是一个未解决的问题。研究了一点,并尝试了一些东西。不幸的是,没有一个工作。
  • 还有一个最后的手段来覆盖redis-rb的ensure_connected-方法,但是我不确定这会有什么副作用

标签: ruby-on-rails redis puma


【解决方案1】:

这就是我所做的:

  Redis.current.client.reconnect
  $redis = Redis.current

($redis 是我的一个 redis 客户端的全局实例)

【讨论】:

    【解决方案2】:

    我能够使用猴子补丁修复错误。这会改变行为,因此它只是重新连接而不是抛出 Redis::InheritedError

    ###### MONKEYPATCH redis-rb 
    # https://github.com/redis/redis-rb/issues/364
    # taken from https://github.com/redis/redis-rb/pull/389/files#diff-597c124889a64c18744b52ef9687c572R314
    class Redis
      class Client
       def ensure_connected
          tries = 0
    
          begin
            if connected?
              if Process.pid != @pid
                reconnect
              end
            else
              connect
            end
    
            tries += 1
    
            yield
          rescue ConnectionError
            disconnect
    
            if tries < 2 && @reconnect
              retry
            else
              raise
            end
          rescue Exception
            disconnect
            raise
          end
        end
      end
    end
    ## MONKEYPATCH end
    

    【讨论】:

      【解决方案3】:

      我已将其放入我的 config/puma.rb 文件中,对我有用。

      on_restart do
        $redis = DiscourseRedis.new
        Discourse::Application.config.cache_store.reconnect
      end
      on_worker_boot do
        $redis = DiscourseRedis.new
        Discourse::Application.config.cache_store.reconnect
      end
      

      【讨论】:

        【解决方案4】:

        我在集群模式下使用 Puma 运行带有 IdentityCache 的 Rails 应用程序,worker=4。

        重新连接必须在 on_worker_boot 回调中进行。

        我必须重新连接 Rails.cache 和 IdentityCache 以避免重新启动错误。这是我的工作:

        puma-config.rb

        on_worker_boot do
           puts 'On worker boot...'
           puts "Reconnecting Rails.cache"
           Rails.cache.reconnect
           begin
              puts "Reconnecting IdentityCache"
              IdentityCache.cache.cache_backend.reconnect
           rescue Exception => e
              puts "Error trying to reconnect identity_cache_store: #{e.message}"
           end
        end
        

        然后我在日志中看到以下内容,向我展示了一切正常的证据。

        On worker boot...
        Reconnecting Rails.cache
        Reconnecting IdentityCache
        On worker boot...
        Reconnecting Rails.cache
        Reconnecting IdentityCache
        On worker boot...
        Reconnecting Rails.cache
        Reconnecting IdentityCache
        On worker boot...
        Reconnecting Rails.cache
        Reconnecting IdentityCache
        [7109] - Worker 7115 booted, phase: 0
        [7109] - Worker 7123 booted, phase: 0
        [7109] - Worker 7119 booted, phase: 0
        [7109] - Worker 7127 booted, phase: 0
        

        果然,以前服务器重启后出现的第一个请求问题都没有了。 QED。

        【讨论】:

          【解决方案5】:
          1. redis-rb升级到3.1.0或以上。 详情https://github.com/redis/redis-rb/pull/414/files#
          2. 猴子补丁
          # https://github.com/redis/redis-rb/pull/414/files#diff-5bc007010e6c2e0aa70b64d6f87985c20986ee1b2882b63a89b52659ee9c91f8
          class Redis
            class Client
              def ensure_connected
                tries = 0
                begin
                  if connected?
                    if Process.pid != @pid
                      raise InheritedError,
                        "Tried to use a connection from a child process without reconnecting. " +
                        "You need to reconnect to Redis after forking."
                    end
                  else
                    connect
                  end
                  tries += 1
          
                  yield
                rescue ConnectionError, InheritedError
                  disconnect
          
                  if tries < 2 && @reconnect
                    retry
                  else
                    raise
                  end
                rescue Exception
                  disconnect
                  raise
                end
              end
            end
          end
          

          【讨论】:

            猜你喜欢
            • 2016-03-17
            • 2015-10-26
            • 2013-08-09
            • 1970-01-01
            • 2015-07-08
            • 2021-06-27
            • 2020-07-27
            • 1970-01-01
            • 2018-03-27
            相关资源
            最近更新 更多