簡體   English   中英

Puma fork 后重新連接Redis

[英]Reconnect with Redis after Puma fork

我在 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.)這樣的錯誤Redis::InheritedError (Tried to use a connection from a child process without reconnecting. You need to reconnect to Redis after forking.)

使用 google 和 stackoverflow 四處搜索讓我認為在 puma 分叉子進程后我需要重新連接到 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.)引起的“內部服務器錯誤” 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

那沒有用。

我在config/initializers/redis.rb嘗試在$redis.ping之后Redis.new$redis.ping Redis.new 那也沒有用。

如果 puma 在沒有運行 puma 進程的情況下啟動,或者在 puma 進程的實例運行時重新啟動,我會收到此錯誤。

刷新頁面會讓我克服這個錯誤。 但即使在第一次嘗試使用$redis我也想擺脫這個。 我在想我沒有使用redis gem 或正確配置它的重新連接。 誰能告訴我:

  1. 這是在 rails 應用程序中使用redis gem 的正確方法嗎?
  2. puma redis連接應該如何重新連接?

puma gem 文檔說,“你應該在這個塊中放置代碼來關閉全局日志文件、redis 連接等,這樣它們的文件描述符就不會泄漏到重新啟動的進程中。否則將導致描述符慢慢耗盡,由於服務器多次重啟,最終模糊了崩潰。” 它在談論on_restart塊。 但它沒有說明應該如何做。

我能用monkeypatch修復錯誤。 這會改變行為,因此它只是重新連接而不是拋出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

我在集群模式下使用Puma運行帶有IdentityCache的Rails應用程序,其中workers = 4。

重新連接必須在on_worker_boot回調中發生。

我必須重新連接Rails.cache和IdentityCache以避免重啟錯誤。 這就是我的工作:

彪馬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。

這是我做的:

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

($ redis是我的redis客戶端的全局實例)

我把它放到我的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. 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM