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


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

Переменная класса может быть разделена между двумя запросами к моему 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: На самом деле эта опция перезагружает класс, но не решает проблему параллелизма в потоке. Переменные классов сбрасываются, но они могут быть изменены другими нитка.

Как threadsafe классы переменных?

1 2

1 ответ:

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

Мой вариант использования-это добавление методов в класс модели пользователя, таких как 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

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

Мои настройки разработки зеркально отражают мои производственные настройки wrt class handling, что не удобно (требует перезагрузки сервера после изменения), но более удобно, чем гоняться за несуществующими ошибками. Драгоценный каменьrerun может мониторинг изменений файловой системы и перезагрузка сервера для вас, так что вы получите надежную обработку изменений в процессе разработки, хотя и медленнее, чем rails broken class reloading.

Конфигурация / среда / разработка.РБ:

# 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

Q: Как threadsafe классы переменных?

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

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

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