如何使用Rails进行线程安全单例,如何保持我的类变量安全?

时间:2014-09-23 14:49:44

标签: ruby-on-rails-4 thread-safety singleton static-variables class-variables

我已阅读Is Rails shared-nothing or can separate requests access the same runtime variables?并解释了我的问题:

类变量可能在我的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)启动此代码,我希望每次在rails服务器的日志中读取“false”。但是,默认情况下它只是第一次“假”。

编辑2: 实际上这个选项重装了类,但没有解决线程中的concurency问题。 类变量被重置,但它们可以被其他线程修改。

线程安全类变量如何?

1 个答案:

答案 0 :(得分:5)

我使用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以确保下载类也被加载。

我的开发设置镜像我的生产设置wrt类处理这不方便(需要在更改后重新启动服务器)但比追逐不存在的错误更方便。 rerun gem可以监视文件系统更改并为您重新启动服务器,以便在开发过程中获得可靠的更改处理,尽管比重新下载的类更糟糕。

<强>配置/环境/ 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的线程测试了并行CPU密集型代码,并没有完全没有并行性。每个核心使用1个ruby进程,我得到了真正的并行性。

我会认真考虑使用多个进程进行精简,然后决定是否要使用Thin + EventMachine来提高每个进程的总吞吐量。