Повторное подключение к Redis после форка Puma

Я использую глобальную переменную в приложении rails для хранения клиента Redis с помощью гема Redis. В config/initializers/redis.rb у меня есть

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

Затем в коде приложения я использую $redis для работы с данными в хранилище Redis.

Я также использую puma в качестве веб-сервера в рабочей среде и capistrano для развертывания кода. В процессе развертывания capistrano перезапускает puma.

Каждый раз, когда я запускаю или перезапускаю веб-серверы puma, я всегда получаю «Внутреннюю ошибку сервера», когда впервые использую $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 привел меня к мысли, что мне нужно повторно подключиться к Redis после того, как puma разветвит дочерние процессы. Итак, я добавил в свой 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

Это не сработало.

Я пробовал с config/initializers/redis.rb по $redis.ping сразу после Redis.new. Это тоже не сработало.

Я получил эту ошибку, если puma была запущена без запущенных процессов puma или перезапущена, когда запущен экземпляр процесса puma.

Обновление страницы избавит меня от этой ошибки. Но я хочу избавиться от этого еще при первой попытке использовать $redis. Я думал, что я не использовал гем redis или неправильно настроил его переподключение. Может ли кто-нибудь сказать мне:

  1. Это правильный способ использования redis gem в приложении rails?
  2. Как переподключить соединение redis в puma?

В документации puma gem говорится: «Вы должны поместить в этот блок код для закрытия глобальных файлов журналов, соединений Redis и т. д., чтобы их файловые дескрипторы не просачивались в перезапущенный процесс. в конечном итоге неясные сбои, поскольку сервер много раз перезагружается». Речь шла о блоке on_restart. Но не сказано, как это должно быть сделано.


person hau    schedule 10.09.2013    source источник
comment
У меня такая же ошибка и я не могу заставить ее работать. Не могли бы вы найти какой-либо ответ до сих пор?   -  person Simon Woker    schedule 12.09.2013
comment
Нет, для меня это все еще нерешенный вопрос. Изучил немного больше и попробовал несколько вещей. Ни один не работал к сожалению.   -  person hau    schedule 12.09.2013
comment
Есть еще последнее средство, чтобы перезаписать ensure_connected-метод redis-rb, но я не уверен в побочных эффектах, которые это будет иметь   -  person Simon Woker    schedule 12.09.2013


Ответы (4)


Я смог исправить ошибку с помощью обезьяньего патча. Это изменяет поведение, так что он просто переподключается вместо того, чтобы бросать 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
person Simon Woker    schedule 12.12.2013

Я запускаю приложение Rails с IdentityCache, используя Puma в кластерном режиме с рабочими = 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

Конечно же, проблемы с первым запросом, которые раньше были после перезагрузки сервера, исчезли. КЭД.

person Darren Hicks    schedule 01.04.2014

Вот что я сделал:

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

($redis — мой глобальный экземпляр клиента Redis)

person Duke    schedule 24.09.2013

Я поместил это в свой файл 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
person ifyouseewendy    schedule 14.02.2014