由于Thin / Unicorn是单线程的,你如何处理Thread.current / per-request存储?
只是运行一个简单的测试 - 在一个会话中设置一个密钥,从另一个会话中读取它 - 看起来它始终从同一个地方写入/读取。但不会发生在WEBrick上。
class TestController < ApplicationController
def get
render text: Thread.current[:xxx].inspect
end
def set
Thread.current[:xxx] = 1
render text: "SET to #{Thread.current[:xxx]}"
end
end
尝试将config.threadsafe!
添加到application.rb,无需更改。
存储每个请求数据的正确方法是什么?
为什么有使用Thread.current存储的宝石(包括Rails本身和倾斜)?他们如何克服这个问题?
对于每个请求,Thread.current 是否是安全的,但是在请求之后不能清除,我自己需要这样做吗?
总结以下与@skalee和@JesseWolgamott以及我的调查结果的讨论 -
Thread.current取决于运行应用程序的服务器。虽然服务器可能确保在同一个Thread.current上没有同时运行两个请求,但是这个哈希中的值可能不会在请求之间被清除,因此在使用时 - 必须设置初始值覆盖最后一个值。
有一些众所周知的宝石使用Thread.current,如Rails,倾斜和布料。我猜测如果它被禁止或不安全,他们就不会使用它。在使用散列上的任何键之前,它们似乎都设置了一个值(甚至在请求结束后将其设置回原始值)。
但总的来说,Thread.current不是每个请求存储的最佳实践。对于大多数情况,更好的设计会做,但在某些情况下,使用env
会有所帮助。它可以在控制器中使用,也可以在中间件中使用,并且可以注入应用程序中的任何位置。
更新2 - 似乎现在,draper正在使用Thread.current错误。见https://github.com/drapergem/draper/issues/390
更新3 - 修正了错误。
答案 0 :(得分:0)
您通常希望在会话中存储内容。如果你想要一些非常短暂的生活,请参阅Rails' flash。每次请求都清除它。任何依赖线程的方法都不会在不同的Web服务器上一致地工作。
另一种选择是修改env
哈希:
env['some_number'] = 5
BTW Unicorn不仅仅是单线程,而是分叉。每个请求都会产生新进程(尽管它听起来很吓人,但在Linux上效率很高)。因此,如果您在Unicorn中设置任何内容,甚至是全局变量,它将不会持久存储到另一个请求。
答案 1 :(得分:0)
虽然人们仍然警告不要使用Thread.current
存储“线程全局”数据,但在Rails中执行此操作的正确方法是使用Rack中间件清除Thread.current
对象。 Steve Labnik编写了request_store gem来轻松完成这项工作。宝石的源代码非常非常小,我建议你阅读它。
有趣的部分转载如下。
module RequestStore
def self.store
Thread.current[:request_store] ||= {}
end
def self.clear!
Thread.current[:request_store] = {}
end
end
module RequestStore
class Middleware
def initialize(app)
@app = app
end
def call(env)
RequestStore.clear!
@app.call(env)
end
end
end
请注意,清除整个 Thread.current
不是一个好习惯。 request_store基本上是做什么的,它是跟踪你的应用程序存入Thread.current的密钥,并在请求完成后清除它。
答案 2 :(得分:0)
使用Thread.current
的一个注意事项是,对于重用线程或拥有线程池的服务器,在每次请求后进行清理变得非常重要。
这正是request_store
gem提供的内容,一个类似于Thread.current
的简单API,用于在每次请求后清理商店数据。
RequestStore[:items] = []
请注意,gem使用Thread.current
来保存商店,因此它在多线程环境中无法正常工作,每个请求都有多个线程。
为了解决这个问题,我实现了一个可以在线程之间共享同一请求的商店。它被称为request_store_rails
,用法非常相似:
RequestLocals[:items] = []