Redis订阅回调块内调用ActiveJob perform_later报NoMethodError如何解决
问题原因分析
redis-rb客户端调用subscribe系列方法后,当前连接会切换为专用的订阅模式,返回Redis::SubscribedClient实例,该实例仅支持订阅/退订类命令,不支持普通Redis读写操作。而perform_later入队Sidekiq任务本质是向Redis写入队列数据,若Sidekiq复用了当前处于订阅模式的Redis连接,就会抛出NoMethodError提示不存在call_pipeline方法。
解决方案
核心逻辑是使用两个完全独立的Redis连接:一个专属用于订阅操作,另一个用于普通Redis读写(包括Sidekiq入队)。Sidekiq默认会维护独立的连接池,只要不手动将订阅连接赋值给Sidekiq就可以直接使用,实现订阅成功后才入队任务的需求。
参考实现代码:
# 1. 初始化独立的订阅专用Redis连接,不要和Sidekiq的连接池混用 sub_redis = Redis.new( host: ENV["REDIS_HOST"], port: ENV["REDIS_PORT"], password: ENV["REDIS_PASSWORD"] ) # 2. 执行订阅逻辑,监听订阅成功事件 sub_redis.subscribe("target_channel") do |on| # 订阅成功回调,触发时代表频道已经订阅完成 on.subscribe do |channel, subscribed_count| # 此处调用perform_later使用Sidekiq自己的独立连接池,不会用到订阅连接 YourCustomJob.perform_later(channel) end # 处理订阅到的消息 on.message do |channel, message| # 业务侧消息处理逻辑 end end
注意事项
- 订阅逻辑需要运行在独立的进程/线程中,不要嵌入Rails Web请求的处理流程,否则会阻塞请求线程导致超时
- 不要在订阅连接上执行任何非订阅类的Redis操作,直到调用
unsubscribe退出订阅模式后才能恢复普通功能 - 如果需要自定义Sidekiq的Redis连接,确保配置的是独立的连接实例,不要复用订阅用的Redis对象
内容的提问来源于stack exchange,提问作者Echolot
相关产品推荐
相关产品推荐

