在多线程Rails环境中使用Redis的最佳方法是什么?(Puma/Sidekiq)

Dan*_*ola 29 ruby thread-safety redis sidekiq ruby-on-rails-4

我在我的应用程序中使用Redis,包括Sidekiq队列和模型缓存.

考虑到将从我的Web应用程序(通过Puma运行)和Sidekiq内部的后台作业调用将要访问Redis的模型,我的模型可以使用Redis连接的最佳方法是什么?

我目前在我的初始化器中执行此操作:

Redis.current = Redis.new(host: 'localhost', port: 6379)
Run Code Online (Sandbox Code Playgroud)

然后在整个代码中使用Redis.current.get/ Redis.current.set(和类似的)...

据我所知,这应该是线程安全的,因为Redis客户端只使用Monitor一次运行一个命令.

现在,Sidekiq拥有自己的Redis连接池,并建议这样做

Sidekiq.redis do |conn|
   conn.get
   conn.set
end
Run Code Online (Sandbox Code Playgroud)

据我所知,这比使用Redis.current的方法更好,因为当他们命中Redis时,多个线程上没有多个工作线在一个连接上互相等待.

但是,如何才能将我从Sidekiq.redis获得的连接提供给我的模型?(无需在每个方法调用中将其作为参数传递)

我无法在该块中设置Redis.current,因为它是全局的,并且我回到每个人使用相同的连接(以及它们之间随机切换,甚至可能是非线程安全的)

我应该将从Sidekiq.Redis获得的连接存储到Thread-local变量中,并在任何地方使用该线程局部变量?

在这种情况下,我在"Puma"环境中做了什么?如何设置线程局部变量?

对此有任何想法非常感谢.

谢谢!

Mik*_*ham 40

您为应用程序代码使用单独的全局连接池.在你的redis.rb初始化程序中放置这样的东西:

require 'connection_pool'
REDIS = ConnectionPool.new(size: 10) { Redis.new }
Run Code Online (Sandbox Code Playgroud)

现在在任何地方的应用程序代码中,您可以这样做:

REDIS.with do |conn|
  # some redis operations
end
Run Code Online (Sandbox Code Playgroud)

您将拥有多达10个连接,可以在您的puma/sidekiq工作人员中分享.这将导致更好的性能,因为正如您正确指出的那样,您不会让所有线程都在单个Redis连接上.

所有这些都记录在这里:https://github.com/mperham/sidekiq/wiki/Advanced-Options#connection-pooling

  • 现在,在这种情况下,我将为代码提供自己的连接池(在您的示例中为10个连接),Sidekiq将具有自己的连接池,该连接池将用于其“工人工作”,不会在我的代码中的任何地方使用“ Sidekiq.redis”。它是否正确? (2认同)