Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 使用sidekiq处理两个单独的redis实例?_Ruby On Rails_Redis_Queue_Sidekiq - Fatal编程技术网

Ruby on rails 使用sidekiq处理两个单独的redis实例?

Ruby on rails 使用sidekiq处理两个单独的redis实例?,ruby-on-rails,redis,queue,sidekiq,Ruby On Rails,Redis,Queue,Sidekiq,下午好 我有两个独立但相关的应用程序。它们都应该有自己的后台队列(读:单独的Sidekiq和Redis进程)。但是,我希望偶尔能够将作业从app1推送到app2的队列中 从简单的队列/推送角度来看,如果app1没有现有的Sidekiq/Redis堆栈,则很容易做到这一点: # In a process, far far away # Configure client Sidekiq.configure_client do |config| config.redis = { :url =&

下午好

我有两个独立但相关的应用程序。它们都应该有自己的后台队列(读:单独的Sidekiq和Redis进程)。但是,我希望偶尔能够将作业从
app1
推送到
app2
的队列中

从简单的队列/推送角度来看,如果
app1
没有现有的Sidekiq/Redis堆栈,则很容易做到这一点:

# In a process, far far away

# Configure client 
Sidekiq.configure_client do |config|
  config.redis = { :url => 'redis://redis.example.com:7372/12', :namespace => 'mynamespace' }
end

# Push jobs without class definition 
Sidekiq::Client.push('class' => 'Example::Workers::Trace', 'args' => ['hello!'])

# Push jobs overriding default's 
Sidekiq::Client.push('queue' => 'example', 'retry' => 3, 'class' =>     'Example::Workers::Trace', 'args' => ['hello!'])
然而,考虑到我已经从
app1
调用了
Sidekiq.configure_client
Sidekiq.configure_server
,在这两者之间可能有一步需要做些事情

显然,我可以直接从Sidekiq内部获取序列化和规范化代码,然后手动推送到
app2
的redis队列,但这似乎是一个脆弱的解决方案。我希望能够使用
Client.push
功能

我想我的理想解决方案是:

SidekiqTWO.configure_客户机{远程连接…}
SidekiqTWO::Client.push(作业…)

甚至:

$redis\u remote=远程连接…..

Sidekiq::Client.push(作业$redis\u remote)

显然有点滑稽,但这是我的理想用例

谢谢

所以有一件事是,“Sidekiq消息格式非常简单且稳定:它只是JSON格式的散列。”我的重点是——我不认为向Sidekiq发送JSON太脆弱。特别是当您想要细粒度地控制将作业发送到哪个Redis实例时,就像在OP的情况下一样,我可能只需要编写一个小包装器,让我在作业排队时指示一个Redis实例

对于Kevin Bedell将作业循环到Redis实例的更一般的情况,我认为您不想控制使用哪个Redis实例——您只想排队并自动管理分发。它看起来像,并且使用:


在获取高可用性和故障转移的过程中要考虑的另一件事是获得可靠性特性:“SIDKIQ PRO客户端可以承受短暂的ReIIS中断。它将在错误时本地输入作业,并在连接恢复后尝试交付这些作业。”如果Redis实例发生故障,短暂的延迟不会影响您的应用程序。如果您的两个Redis实例中有一个出现故障,而您正在使用循环,那么您仍然会失去一些工作,除非您正在使用此功能。

正如Carols所说,这非常简单,但由于我总是喜欢封装此功能,并能够在其他项目中重用它,因此我从一个新的角度更新了一个想法。以下解决方案改进了Rails 4.1和Spring preloader中无法生存的Hotel今晚的解决方案

目前,我将以下文件添加到
lib/remote\u sidekiq/

remote_sidekiq.rb

class RemoteSidekiq
  class_attribute :redis_pool
end
require 'sidekiq'
require 'sidekiq/client'

module RemoteSidekiqWorker
  def client
    pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
    Sidekiq::Client.new(pool)
  end

  def push(worker_name, attrs = [], queue_name = "default")
    client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
  end
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
class RemoteSidekiq
  class_attribute :redis_pool
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
require 'sidekiq'

module ActiveJob
  module QueueAdapters
    class RemoteSidekiqAdapter
      def enqueue(job)
        #Sidekiq::Client does not support symbols as keys
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ]
      end

      def enqueue_at(job, timestamp)
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ],
          "at"      => timestamp
      end

      def client
        @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
      end
    end
  end
end
remote\u sidekiq\u worker.rb

class RemoteSidekiq
  class_attribute :redis_pool
end
require 'sidekiq'
require 'sidekiq/client'

module RemoteSidekiqWorker
  def client
    pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
    Sidekiq::Client.new(pool)
  end

  def push(worker_name, attrs = [], queue_name = "default")
    client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
  end
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
class RemoteSidekiq
  class_attribute :redis_pool
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
require 'sidekiq'

module ActiveJob
  module QueueAdapters
    class RemoteSidekiqAdapter
      def enqueue(job)
        #Sidekiq::Client does not support symbols as keys
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ]
      end

      def enqueue_at(job, timestamp)
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ],
          "at"      => timestamp
      end

      def client
        @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
      end
    end
  end
end
您需要创建一个初始值设定项来设置redis_池

config/initializers/remote_sidekiq.rb

class RemoteSidekiq
  class_attribute :redis_pool
end
require 'sidekiq'
require 'sidekiq/client'

module RemoteSidekiqWorker
  def client
    pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
    Sidekiq::Client.new(pool)
  end

  def push(worker_name, attrs = [], queue_name = "default")
    client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
  end
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
class RemoteSidekiq
  class_attribute :redis_pool
end
url = ENV.fetch("REDISCLOUD_URL")
namespace = 'primary'

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
require 'sidekiq'

module ActiveJob
  module QueueAdapters
    class RemoteSidekiqAdapter
      def enqueue(job)
        #Sidekiq::Client does not support symbols as keys
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ]
      end

      def enqueue_at(job, timestamp)
        job.provider_job_id = client.push \
          "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
          "wrapped" => job.class.to_s,
          "queue"   => job.queue_name,
          "args"    => [ job.serialize ],
          "at"      => timestamp
      end

      def client
        @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
      end
    end
  end
end
由Aleks编辑:

在sidekiq的never版本中,而不是行:

redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
使用行:

redis_remote_options = {
  namespace: "yournamespace",
  url: ENV.fetch("REDISCLOUD_URL")
}

RemoteSidekiq.redis_pool = Sidekiq::RedisConnection.create(redis_remote_options)
然后,您可以在任何需要的地方简单地使用
包含RemoteSidekiqWorker
模块。工作完成了

****适用于更大的环境****

添加RemoteWorker机型可增加额外的好处:

  • 您可以在任何地方重用RemoteWorkers,包括可以访问目标sidekiq workers的系统。这对调用方是透明的。要在目标sidekiq系统中使用“RemoteWorkers”表单,请不要使用初始值设定项,因为它默认使用本地sidekiq客户端
  • 使用RemoteWorkers确保始终发送正确的参数(代码=文档)
  • 通过创建更复杂的Sidekiq架构进行扩展对调用方来说是透明的
  • 下面是一个RemoteWorker示例

    class RemoteTraceWorker
      include RemoteSidekiqWorker
      include ActiveModel::Model
    
      attr_accessor :message
    
      validates :message, presence: true
    
      def perform_async
        if valid?
          push(worker_name, worker_args)
        else
          raise ActiveModel::StrictValidationFailed, errors.full_messages
        end
      end
    
      private
    
      def worker_name
        :TraceWorker.to_s
      end
    
      def worker_args
        [message]
      end
    end
    

    我遇到了这个问题,并遇到了一些问题,因为我使用的是
    ActiveJob
    ,这使消息从队列中读取变得复杂

    基于ARO的回答,您仍然需要redis_池设置:

    remote_sidekiq.rb

    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    require 'sidekiq'
    require 'sidekiq/client'
    
    module RemoteSidekiqWorker
      def client
        pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
        Sidekiq::Client.new(pool)
      end
    
      def push(worker_name, attrs = [], queue_name = "default")
        client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
      end
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    require 'sidekiq'
    
    module ActiveJob
      module QueueAdapters
        class RemoteSidekiqAdapter
          def enqueue(job)
            #Sidekiq::Client does not support symbols as keys
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ]
          end
    
          def enqueue_at(job, timestamp)
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ],
              "at"      => timestamp
          end
    
          def client
            @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
          end
        end
      end
    end
    
    config/initializers/remote_sidekiq.rb

    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    require 'sidekiq'
    require 'sidekiq/client'
    
    module RemoteSidekiqWorker
      def client
        pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
        Sidekiq::Client.new(pool)
      end
    
      def push(worker_name, attrs = [], queue_name = "default")
        client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
      end
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    require 'sidekiq'
    
    module ActiveJob
      module QueueAdapters
        class RemoteSidekiqAdapter
          def enqueue(job)
            #Sidekiq::Client does not support symbols as keys
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ]
          end
    
          def enqueue_at(job, timestamp)
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ],
              "at"      => timestamp
          end
    
          def client
            @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
          end
        end
      end
    end
    
    现在,我们将创建一个ActiveJob适配器来对请求排队,而不是worker:

    lib/active\u job/queue\u adapters/remote\u sidekiq\u adapter.rb

    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    require 'sidekiq'
    require 'sidekiq/client'
    
    module RemoteSidekiqWorker
      def client
        pool = RemoteSidekiq.redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
        Sidekiq::Client.new(pool)
      end
    
      def push(worker_name, attrs = [], queue_name = "default")
        client.push('args' => attrs, 'class' => worker_name, 'queue' => queue_name)
      end
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    class RemoteSidekiq
      class_attribute :redis_pool
    end
    
    url = ENV.fetch("REDISCLOUD_URL")
    namespace = 'primary'
    
    redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))
    
    RemoteSidekiq.redis_pool = ConnectionPool.new(size: ENV['MAX_THREADS'] || 6) { redis }
    
    require 'sidekiq'
    
    module ActiveJob
      module QueueAdapters
        class RemoteSidekiqAdapter
          def enqueue(job)
            #Sidekiq::Client does not support symbols as keys
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ]
          end
    
          def enqueue_at(job, timestamp)
            job.provider_job_id = client.push \
              "class"   => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
              "wrapped" => job.class.to_s,
              "queue"   => job.queue_name,
              "args"    => [ job.serialize ],
              "at"      => timestamp
          end
    
          def client
            @client ||= ::Sidekiq::Client.new(RemoteSidekiq.redis_pool)
          end
        end
      end
    end
    
    我现在可以使用适配器对事件进行排队:

    require 'active_job/queue_adapters/remote_sidekiq_adapter'
    
    class RemoteJob < ActiveJob::Base
      self.queue_adapter = :remote_sidekiq
    
      queue_as :default
    
      def perform(_event_name, _data)
        fail "
          This job should not run here; intended to hook into
          ActiveJob and run in another system
        "
      end
    end
    
    需要“活动作业/队列适配器/远程侧密钥适配器”
    类RemoteJob

    我现在可以使用普通的ActiveJobAPI对作业进行排队。无论哪个应用程序从队列中读取该消息,都需要有一个匹配的
    RemoteJob
    来执行该操作。

    我在这方面提供了200分的奖励——我更感兴趣的是允许一个sidekiq客户端将消息“循环”到两个不同的redis实例中,以实现高可用性和安全性故障转移。谢谢你的精心研究!漂亮的回答