在轨道中使用 Thread.current []的安全性

关于在 Thread.current散列中存储信息的实践(例如,current _ user、当前子域等) ,我总是得到相互矛盾的意见。该技术已被提出作为一种简化模型层后续处理(查询范围、审计等)的方法。

许多人认为这种做法是不可接受的,因为它打破了 MVC 模式。 其他人则对这种方法的可靠性/安全性表示担忧,我的两部分问题集中在后一方面。

  1. Thread.current散列是否保证在整个周期中对一个且仅对一个响应可用和私有?

  2. 我理解,在响应结束时,一个线程很可能被移交给其他传入请求,从而泄露存储在 Thread.current中的任何信息。在响应结束前清除这些信息(例如从控制器的 after_filter执行 Thread.current[:user] = nil)是否足以防止这种安全性破坏?

谢谢! 朱塞佩

28138 次浏览

There is not an specific reason to stay away from thread-local variables, the main issues are:

  • it's harder to test them, as you will have to remember to set the thread-local variables when you're testing out code that uses it
  • classes that use thread locals will need knowledge that these objects are not available to them but inside a thread-local variable and this kind of indirection usually breaks the law of demeter
  • not cleaning up thread-locals might be an issue if your framework reuses threads (the thread-local variable would be already initiated and code that relies on ||= calls to initialize variables might fail

So, while it's not completely out of question to use, the best approach is not to use them, but from time to time you hit a wall where a thread local is going to be the simplest possible solution without changing quite a lot of code and you will have to compromise, have a less than perfect object oriented model with the thread local or changing quite a lot of code to do the same.

So, it's mostly a matter of thinking which is going to be the best solution for your case and if you're really going down the thread-local path, I'd surely advise you to do it with blocks that remember to clean up after they are done, like the following:

around_filter :do_with_current_user


def do_with_current_user
Thread.current[:current_user] = self.current_user
begin
yield
ensure
Thread.current[:current_user] = nil
end
end

This ensures the thread local variable is cleaned up before being used if this thread is recycled.

This little gem ensures your thread/request local variables not stick between requests: https://github.com/steveklabnik/request_store

The accepted answer is technically accurate, but as pointed out in the answer gently, and in http://m.onkey.org/thread-safety-for-your-rails not so gently:

Don't use thread local storage, Thread.current if you don't absolutely have to

The gem for request_store is another solution (better) but just read the readme there for more reasons to stay away from thread local storage.

There is almost always a better way.

The accepted answer covers the question but as Rails 5 now provides a "Abstract super class" ActiveSupport::CurrentAttributes which uses Thread.current.

I thought I would provide a link to that as a possible(unpopular) solution.

https://github.com/rails/rails/blob/master/activesupport/lib/active_support/current_attributes.rb