简体   繁体   English

Puma fork 后重新连接Redis

[英]Reconnect with Redis after Puma fork

I am using a global variable in a rails application to store a redis client using the redis gem .我在 rails 应用程序中使用全局变量来存储使用redis gem的 redis 客户端。 In a config/initializers/redis.rb , I haveconfig/initializers/redis.rb ,我有

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

Then in application code, I use $redis to work the data in the Redis store.然后在应用程序代码中,我使用$redis来处理 Redis 存储中的数据。

I also use puma as the web server in production environment, and capistrano to deploy code.我也在生产环境中使用puma作为 web 服务器,使用 capistrano 来部署代码。 In the deploy process, capistrano restarts puma.在部署过程中,capistrano 会重启 puma。

Every time I start or restart the puma web servers, I always get an "Internal Server Error" when I first use $redis to access data in the Redis store.每次启动或重新启动 puma Web 服务器时,当我第一次使用$redis访问 Redis 存储中的数据时,总是会出现“内部服务器错误”。 I saw errors like Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)我看到了像Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)这样的错误Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)

Searching around with google and stackoverflow led me to think that I needed to reconnect to Redis after puma forks child processes.使用 google 和 stackoverflow 四处搜索让我认为在 puma 分叉子进程后我需要重新连接到 Redis。 So, I added in my config/puma.rb :所以,我在我的config/puma.rb添加:

on_worker_boot do
  $redis.ping
end

But I was still getting the "Internal Server Error" caused by Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.) .但是我仍然收到由Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)引起的“内部服务器错误” Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)

I saw this post http://qiita.com/yaotti/items/18433802bf1720fc0c53 .我看到了这个帖子http://qiita.com/yaotti/items/18433802bf1720fc0c53 I then tried adding in config/puma.rb :然后我尝试添加config/puma.rb

on_restart do
  $redis.quit
end

That did not work.那没有用。

I tried in config/initializers/redis.rb to $redis.ping right after Redis.new .我在config/initializers/redis.rb尝试在$redis.ping之后Redis.new$redis.ping Redis.new That did not work either.那也没有用。

I got this error if puma was started with no puma processes running, or restarted when an instance of puma process was running.如果 puma 在没有运行 puma 进程的情况下启动,或者在 puma 进程的实例运行时重新启动,我会收到此错误。

Refreshing the page would get me past this error.刷新页面会让我克服这个错误。 But I want to get rid of this even on the first attempt to use $redis .但即使在第一次尝试使用$redis我也想摆脱这个。 I was thinking that I did not use the redis gem or configure its reconnection correctly.我在想我没有使用redis gem 或正确配置它的重新连接。 Could someone tell me:谁能告诉我:

  1. Is that the right way to use redis gem in a rails application?这是在 rails 应用程序中使用redis gem 的正确方法吗?
  2. How should the redis connection be reconnected in puma ?puma redis连接应该如何重新连接?

puma gem documentation says, "You should place code to close global log files, redis connections, etc in this block so that their file descriptors don't leak into the restarted process. Failure to do so will result in slowly running out of descriptors and eventually obscure crashes as the server is restart many times." puma gem 文档说,“你应该在这个块中放置代码来关闭全局日志文件、redis 连接等,这样它们的文件描述符就不会泄漏到重新启动的进程中。否则将导致描述符慢慢耗尽,由于服务器多次重启,最终模糊了崩溃。” It was talking about the on_restart block.它在谈论on_restart块。 But it did not say how that should be done.但它没有说明应该如何做。

I was able to fix the error with a monkeypatch. 我能用monkeypatch修复错误。 This changes the behaviour so it just reconnects instead of throwing the Redis::InheritedError 这会改变行为,因此它只是重新连接而不是抛出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

I'm running a Rails Application with IdentityCache using Puma in clustered mode with workers=4. 我在集群模式下使用Puma运行带有IdentityCache的Rails应用程序,其中workers = 4。

It is essential that the reconnects happen in the on_worker_boot callback. 重新连接必须在on_worker_boot回调中发生。

I have to reconnect both the Rails.cache and the IdentityCache to avoid restart errors. 我必须重新连接Rails.cache和IdentityCache以避免重启错误。 Here's what I got working: 这就是我的工作:

puma-config.rb 彪马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

Then I see the following in my logs, showing me the proof that it all works. 然后我在日志中看到以下内容,向我展示了一切正常的证明。

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

Sure enough, the first request problems that used to be there after server restart are gone. 果然,服务器重启后的第一个请求问题就消失了。 QED. QED。

Here's what I did: 这是我做的:

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

($redis is my global instance of a redis client) ($ redis是我的redis客户端的全局实例)

I've put this into my config/puma.rb file, works for me. 我把它放到我的config/puma.rb文件中,对我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
  1. Upgrade redis-rb to 3.1.0 or above.redis-rb升级到 3.1.0 或更高版本。 The detail https://github.com/redis/redis-rb/pull/414/files#详情https://github.com/redis/redis-rb/pull/414/files#
  2. monkey patch猴子补丁
# 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM