Как я могу сделать потокобезопасный синглтон с Rails, как обеспечить безопасность переменных моих классов?

Я прочитал Общий доступ к Rails -Ничего или отдельные запросы могут обращаться к одним и тем же переменным времени выполнения? и они объясняют мою проблему:

Переменная класса может быть разделена между двумя запросами к моему rails srver, но где решение !?

Как я могу реализовать безопасный синглтон между запросами?

class Foo
  @@instances = []
end

Как я могу быть уверен, что экземпляры будут сбрасываться для каждого HTTP-запроса ?!

РЕДАКТИРОВАТЬ:

Я нашел решение "config.reload_classes_only_on_change = false", но не уверен, что оно лучше всего для производительности.
Каковы последствия этого варианта?

У меня есть пример для проверки переменных безопасных классов:

class Test
   def self.log
      @test ||= false
      puts @test
      @test = true
   end
end


class ApplicationController < ActionController::Base
   def index
      Test.log
      Test.log
   end
end

если я запускаю этот код с действия перезагрузки (F5), я хочу каждый раз читать "false" в журнале сервера rails. Но по умолчанию «ложно» только в первый раз.

РЕДАКТИРОВАТЬ 2: Фактически, этот параметр перезагружает класс, но не решает проблему параллелизма в потоке. Переменные классов сбрасываются, но они могут быть изменены другим потоком.

Как потокобезопасные переменные классов?


person Matrix    schedule 23.09.2014    source источник


Ответы (1)


Я использую гем request_store, он отлично работает.

Мой вариант использования - добавление методов к классу модели пользователя, таких как current_user, их языковой стандарт, местоположение и т. Д., Поскольку часто другие мои модели нуждаются в этой информации.

Я просто настраиваю текущего пользователя из своего контроллера приложения:

User.current = the_authenticated_user
User.request = request

И в моем классе модели пользователя:

class User
  def self.current
    RequestStore.store[:current_user]
  end

  def self.current=(user)
    RequestStore.store[:current_user] = user
  end

  def self.request
    RequestStore.store[:current_request]
  end

  def self.request=(request)
    # stash the request so things like IP address and GEO-IP based location is available to other models
    RequestStore.store[:current_request] = request
  end

  def self.location
    # resolve the location just once per request
    RequestStore.store[:current_location] ||= self.request.try(:location)
  end
end

Я не включаю опцию перезагрузки классов, так как она вызывает слишком много проблем, я видел несколько версий классов. Если вы используете наследование модели (например, STI), отложенная загрузка и / или загрузка динамического класса часто нарушает способ разрешения классов модели. Вам необходимо использовать require_dependency в базовых и промежуточных классах модели, чтобы гарантировать загрузку последующих классов.

Мои настройки разработки отражают мои производственные настройки с обработкой классов, что неудобно (требует перезапуска сервера после изменения), но более удобно, чем преследование несуществующих ошибок. Гем rerun может отслеживать изменения файловой системы и перезапускать сервер за вас, чтобы вы могли надежно обрабатывать изменения в развитие, хотя и медленнее, чем рельсы с перезарядкой сломанного класса.

config / environment / development.rb:

# Rails class reloading is broken, anytime a class references another you get multiple
# class instances for the same named class and that breaks everything. This is especially
# important in Sequel as models resolve classes once.
# So always cache classes (true)
config.cache_classes = true

# Always eager load so that all model classes are known and STI works
config.eager_load = true

В: Каким образом потокобезопасные переменные классов?

О: Никакие переменные не являются потокобезопасными, если они не защищены synchronize.

С архитектурной точки зрения многопоточность в Rails - пустая трата времени. Единственный способ добиться истинной параллельной производительности / параллелизма - это несколько процессов. Это также позволяет избежать накладных расходов, связанных с блокировкой и потоковой передачей, которых просто нет при длительных процессах. Я тестировал код, интенсивно использующий параллельный ЦП, используя потоки с Ruby 2.x, и не обнаружил никакого параллелизма. С 1 рубиновым процессом на ядро ​​я получил настоящий параллелизм.

Я бы серьезно рассмотрел Thin с несколькими процессами, а затем решил, хотите ли вы использовать Thin + EventMachine для увеличения общей пропускной способности каждого процесса.

person Andrew Hacking    schedule 02.01.2015
comment
Я нашел решение с Thread.current, например: pastebin.com/QzcpQe2h (если у нас есть экземпляр uniq основного контроллера , Я могу добавить в него переменную экземпляра для хранения моих данных uniq), но gem более элегантны ^^ thx. Что касается параметров перезагрузки класса, можете ли вы дать явный ответ: не включать ‹=› false? == ›config.reload_classes_only_on_change = false? А как насчет: config.cache_classes = ??? & config.eager_load = ??? - person Matrix; 04.01.2015
comment
Обновленный ответ. Имейте в виду, что Thread.current не будет очищать локальное хранилище потока по завершении запроса, поэтому у вас может быть состояние, память ресурсов и другие вещи, если вы не будете осторожны, поэтому мы рекомендуем этот ультра-крошечный, но полезный драгоценный камень. - person Andrew Hacking; 05.01.2015
comment
Я не понимаю: это потокобезопасно или нет? Если мы очищаем все переменные, добавленные мной в Thread.current в начале каждого запроса (как gem), хорошо, нет? - person Matrix; 05.01.2015
comment
Установка TLS при запуске запроса - это нормально, и это будет потокобезопасным. Но когда запрос завершится, поток, обработавший запрос, по-прежнему будет зависать от ресурсов, связанных с запросом. Конечно, он будет сброшен в какой-то момент в будущем, когда поток будет использоваться для обработки нового запроса, но лучше иметь надлежащее закрытие запроса, а не удерживать ресурсы. Это также означает, что ваши запросы начинаются с пустого состояния, и нет шансов, что предыдущее состояние / пользователь и т. Д. Могут быть случайно использованы. - person Andrew Hacking; 05.01.2015
comment
пардон, а TLS? Я понимаю идею: очищать все ресурсы между запросами, но что вы предлагаете сделать больше, чем использовать гем request_store? Вы не спрашиваете меня о конфигурации (см. Мой первый комментарий). Спасибо - person Matrix; 05.01.2015
comment
TLS - локальное хранилище потоков. Я обновил свой ответ несколько часов назад, включив в него конфигурацию, и уведомил вас об этом. [См. Мой комментарий, 2-я строка сверху]. Вы также можете просмотреть сводку изменений и построчные различия версий по ссылке внизу ответа, в которой указано, что редактировалось xxx часов назад. Я рекомендую использовать гем, чтобы очистка происходила при завершении запроса, а не с использованием указанного вами pastebin. - person Andrew Hacking; 05.01.2015
comment
хорошо, извините, я скучаю по вашему редактированию сообщения. Последнее для меня: в чем разница между конфигурациями config.cache_classes и config.reload_classes_only_on_change? - person Matrix; 05.01.2015
comment
Его игнорируют, если config.cache_classes истинно. См. guides.rubyonrails.org/. - person Andrew Hacking; 05.01.2015