Ruby on rails 线程的安全性。当前[]在rails中的使用

Ruby on rails 线程的安全性。当前[]在rails中的使用,ruby-on-rails,thread-safety,Ruby On Rails,Thread Safety,在线程.current散列(例如,当前用户、当前子域等)中存储信息的做法上,我不断收到相互矛盾的意见。该技术被认为是一种简化模型层中后期处理(查询范围、审核等)的方法 许多人认为这种做法是不可接受的,因为它打破了MVC模式。 其他人对该方法的可靠性/安全性表示担忧,我的问题分为两部分,主要关注后一个方面 线程.current哈希是否保证在其整个周期内对一个且仅一个响应可用且私有 我知道,在响应结束时,一个线程很可能会被移交给其他传入请求,从而泄漏thread.current中存储的任何

线程.current
散列(例如,当前用户、当前子域等)中存储信息的做法上,我不断收到相互矛盾的意见。该技术被认为是一种简化模型层中后期处理(查询范围、审核等)的方法

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

  • 线程.current
    哈希是否保证在其整个周期内对一个且仅一个响应可用且私有

  • 我知道,在响应结束时,一个线程很可能会被移交给其他传入请求,从而泄漏
    thread.current
    中存储的任何信息。在响应结束之前清除此类信息(例如,通过在控制器的
    过滤器之后执行
    线程.current[:user]=nil
    )是否足以防止此类安全漏洞

  • 谢谢!
    Giuseppe

    没有特定的理由远离线程局部变量,主要问题是:

    • 测试它们比较困难,因为在测试使用线程局部变量的代码时,必须记住设置线程局部变量
    • 使用线程局部变量的类需要知道这些对象对它们来说不是可用的,而是在线程局部变量中,这种间接寻址通常会破坏
    • 如果框架重用线程,则不清理线程局部变量可能是一个问题(线程局部变量可能已经启动,依赖|124;=调用初始化变量的代码可能会失败)
    因此,虽然使用它不是完全不可能的,最好的方法是不使用它们,但有时您会遇到这样的问题:线程本地将是最简单的解决方案,而不需要更改大量代码,您将不得不妥协,使用线程的面向对象模型不够完美本地或更改相当多的代码来执行相同的操作

    因此,这主要是一个思考对于您的案例来说哪一个是最好的解决方案的问题,如果您真的要走线程本地路径,我肯定会建议您使用在完成后记得清理的块来执行此操作,如下所示:

    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
    

    这确保了线程局部变量在循环使用之前被清理干净。

    这个小宝石确保线程/请求局部变量不会在请求之间粘滞:

    接受的答案在技术上是准确的,但正如回答中所指出的,在“不那么温和”中:

    不要使用线程本地存储,
    thread.current
    ,如果您不一定非得使用

    gem for是另一个解决方案(更好),但请阅读其中的自述文件,了解更多远离线程本地存储的原因


    几乎总是有更好的方法。

    公认的答案涵盖了这个问题,但Rails 5现在提供了一个使用Thread.current的“抽象超类”

    我想我会提供一个链接,作为一个可能的()解决方案


    查看“线程变脏。当前”这是Jruby的一位作者写的。#1 ROR代码本身使用Thread.current表示I18N和时区。这是否说明了它的保证?#2.如果#1为真,那么就足够了。谢谢,我添加了参考。在您链接的帖子中,示例专门处理控制器层,建议的解决方案是obvi非常合适。不过,我怀疑,大多数人会感兴趣的是一种干净的方式,让模型访问通常不允许他们访问的1-2条信息,而不向每个模型调用添加额外的参数。在这方面,所有那些大的可怕的“远离线程。当前”到目前为止,没有具体原因的警告标志让我感到不确定。感谢使用带有“确保”块的环绕过滤器,如果您不十分小心,它将干扰异常处理/日志记录。请查看Dejan答案中的RequestStore,以获得一个基于中间件的更干净的解决方案。可能是我解决问题的方法,因为我正在拯救exc先期验收(而不仅仅是如上所述的确保)。异常的调用堆栈被重置到我重新引发的位置,弄乱了Rails错误日志和其他地方。我最终手动将捕获的异常转储到日志文件中以保留它,但这很难看。使用基于中间件的方法更干净。这就是为什么上面的代码只有一个
    确保
    阻止,不尝试捕获异常。当您看到它时很简单:)我正在使用Unicorn with Rails多租户应用程序。你能举一个“更好的方式”的例子吗?您发布的链接引发应用程序错误。谢谢