在多线程 Rails 环境中使用 Redis 的最佳方式是啥? (彪马/Sidekiq)
Posted
技术标签:
【中文标题】在多线程 Rails 环境中使用 Redis 的最佳方式是啥? (彪马/Sidekiq)【英文标题】:What is the best way to use Redis in a Multi-threaded Rails environment? (Puma / Sidekiq)在多线程 Rails 环境中使用 Redis 的最佳方式是什么? (彪马/Sidekiq) 【发布时间】:2015-03-22 17:31:42 【问题描述】:我在我的应用程序中使用 Redis,用于 Sidekiq 队列和模型缓存。
考虑到将要访问 Redis 的模型将被我的 Web 应用程序(通过 Puma 运行)和 Sidekiq 内的后台作业调用,那么让我的模型可以使用 Redis 连接的最佳方式是什么?
我目前正在我的初始化程序中这样做:
Redis.current = Redis.new(host: 'localhost', port: 6379)
然后在整个代码中简单地使用Redis.current.get
/ Redis.current.set
(和类似的)......
据我了解,这应该是线程安全的,因为 Redis 客户端一次只运行一个命令,使用监视器。
现在,Sidekiq 有自己的 Redis 连接池,建议这样做
Sidekiq.redis do |conn|
conn.get
conn.set
end
据我了解,这比仅使用 Redis.current 的方法要好,因为您不会让多个线程上的多个工作人员在单个连接上等待 Redis。
但是,如何使我从 Sidekiq.redis 获得的这种连接可用于我的模型? (无需在每个方法调用中将其作为参数传递)
我无法在该块内设置 Redis.current,因为它是全局的,而且我回到使用相同连接的每个人(加上在它们之间随机切换,这甚至可能是非线程安全的)
我是否应该将从 Sidekiq.Redis 获得的连接存储到线程局部变量中,并在任何地方使用该线程局部变量?
在这种情况下,在“Puma”上下文中我该怎么做?如何设置线程局部变量?
非常感谢您对此的任何想法。
谢谢!
【问题讨论】:
【参考方案1】:您为应用程序代码使用单独的全局连接池。在你的 redis.rb 初始化器中加入这样的东西:
require 'connection_pool'
REDIS = ConnectionPool.new(size: 10) Redis.new
现在在您的应用程序代码的任何地方,您都可以这样做:
REDIS.with do |conn|
# some redis operations
end
您最多可以在 puma/sidekiq 工作人员之间共享 10 个连接。这将带来更好的性能,因为正如您正确指出的那样,您不会让所有线程争夺单个 Redis 连接。
所有这些都记录在这里:https://github.com/mperham/sidekiq/wiki/Advanced-Options#connection-pooling
【讨论】:
现在,在这种情况下,我的代码将拥有自己的连接池(在您的示例中,10 个连接),Sidekiq 将拥有自己的连接池,它将用于“工人工作”,我不会在我的代码中的任何地方使用“Sidekiq.redis”。这是正确的吗? 这与 Puma、Redis 和 Sidekiq 完美结合。我的前端订阅了我的一个控制器中的事件流,现在性能要好得多。就像@DanielMagliola 所说,我从不在我的代码中使用 Sidekiq.redis,你指的是应用范围内可用的 REDIS 变量。 @MikePerham 那么我们是否也使用REDIS
初始化sidekiq? ` Sidekiq.configure_server 做 |config| config.redis = REDIS 结束 Sidekiq.configure_client 做 |config| config.redis = REDIS 结束 `
Sidekiq wiki 描述了如何初始化 Redis。你可以直接给它一个连接池,但这是一个高级选项,只有在你知道自己在做什么的情况下才应该使用。
@MikePerham 如果我们必须让 Redis 池已经可用于其他初始化程序,您是否建议在 application.rb 中的 config.before_initialize
中定义 REDIS 常量?以上是关于在多线程 Rails 环境中使用 Redis 的最佳方式是啥? (彪马/Sidekiq)的主要内容,如果未能解决你的问题,请参考以下文章