首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >工作与两个单独的redis实例与侧翼?

工作与两个单独的redis实例与侧翼?
EN

Stack Overflow用户
提问于 2013-02-07 00:47:17
回答 3查看 5.5K关注 0票数 12

下午好,

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

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

代码语言:javascript
复制
# 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!'])

但是,考虑到我已经从Sidekiq.configure_client调用了Sidekiq.configure_serverapp1,在这两者之间可能会有一些事情需要发生。

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

我想我的理想解决办法是:

SidekiqTWO.configure_client { remote connection..... } SidekiqTWO::Client.push(job....)

甚至:

$redis_remote = remote_connection.....

Sidekiq::Client.push(job, $redis_remote)

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

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-11-10 03:40:10

因此,有一件事是,根据常见问题,“Sidekiq消息格式非常简单,并且稳定的:它只是一个JSON格式的哈希。”强调我的--我不认为把JSON发送到sidekiq太脆弱了。特别是当您想要细粒度地控制您将作业发送给哪个Redis实例时,就像在OP的情况下一样,我可能只需要编写一个小小的包装器,它可以让我指示一个Redis实例以及正在排队的作业。

对于凯文·贝德尔( Kevin )更普遍的情况--在Redis实例中循环执行任务--我想你不想让人控制使用Redis实例的是哪个Redis实例--您只想排队并自动管理分发。它看起来像到目前为止,只有一个人要求这样做。,而他们想出了一个解决办法使用Redis::Distributed

代码语言:javascript
复制
datastore_config = YAML.load(ERB.new(File.read(File.join(Rails.root, "config", "redis.yml"))).result)

datastore_config = datastore_config["defaults"].merge(datastore_config[::Rails.env])

if datastore_config[:host].is_a?(Array)
  if datastore_config[:host].length == 1
    datastore_config[:host] = datastore_config[:host].first
  else
    datastore_config = datastore_config[:host].map do |host|
      host_has_port = host =~ /:\d+\z/

      if host_has_port
        "redis://#{host}/#{datastore_config[:db] || 0}"
      else
        "redis://#{host}:#{datastore_config[:port] || 6379}/#{datastore_config[:db] || 0}"
      end
    end
  end
end

Sidekiq.configure_server do |config|
  config.redis = ::ConnectionPool.new(:size => Sidekiq.options[:concurrency] + 2, :timeout => 2) do
    redis = if datastore_config.is_a? Array
      Redis::Distributed.new(datastore_config)
    else
      Redis.new(datastore_config)
    end

    Redis::Namespace.new('resque', :redis => redis)
  end
end

在追求高可用性和故障转移的过程中,需要考虑的另一件事是获取Sidekiq Pro,其中包括可靠性特性:“Sidekiq客户端可以承受短暂的Redis中断。它将在错误的情况下在本地排队,并在连接恢复后尝试交付这些作业。”因为不管怎么说,sidekiq都是用于后台进程的,如果Redis实例发生故障,那么短时间的延迟不会影响应用程序。如果您的两个Redis实例中有一个出现故障,并且正在使用robin,那么除非您使用此功能,否则仍然会失去一些工作。

票数 10
EN

Stack Overflow用户

发布于 2015-04-27 08:49:50

正如carols10cents所说,它非常简单,但由于我总是喜欢封装该功能并能够在其他项目中重用它,所以我从今晚酒店博客中更新了一个想法。以下解决方案改进了Hotel的Rails 4.1 & Spring预加载器。

目前,我需要将以下文件添加到lib/remote_sidekiq/

remote_sidekiq.rb

代码语言:javascript
复制
class RemoteSidekiq
  class_attribute :redis_pool
end

remote_sidekiq_worker.rb

代码语言:javascript
复制
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

您需要创建一个设置redis_pool的初始化程序。

config/initializers/remote_sidekiq.rb

代码语言:javascript
复制
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 }

由Aleks编辑的

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

代码语言:javascript
复制
redis = Redis::Namespace.new(namespace, redis: Redis.new(url: url))

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

使用线路:

代码语言:javascript
复制
redis_remote_options = {
  namespace: "yournamespace",
  url: ENV.fetch("REDISCLOUD_URL")
}

RemoteSidekiq.redis_pool = Sidekiq::RedisConnection.create(redis_remote_options)

然后,您可以在任何您想要的地方简单地使用include RemoteSidekiqWorker模块。工作完成了!

*用于更大的环境*

添加RemoteWorker模型会增加额外的好处:

  1. 您可以在任何地方重用RemoteWorkers,包括访问目标侧翼工作人员的系统。这对调用者来说是透明的。要在目标侧翼系统中使用"RemoteWorkers“表单,不要使用初始化器,因为它将默认使用本地sidekiq客户端。
  2. 使用RemoteWorkers确保始终发送正确的参数(代码=文档)
  3. 通过创建更复杂的Sidekiq体系结构进行扩展对调用方来说是透明的。

下面是一个示例RemoteWorker

代码语言:javascript
复制
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
票数 4
EN

Stack Overflow用户

发布于 2016-10-16 19:54:15

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

在ARO回答的基础上,您仍然需要redis_pool设置:

remote_sidekiq.rb

代码语言:javascript
复制
class RemoteSidekiq
  class_attribute :redis_pool
end

config/initializers/remote_sidekiq.rb

代码语言:javascript
复制
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 }

现在,我们将创建一个ActiveJob适配器来对请求进行排队,而不是工作人员:

lib/active_job/queue_adapters/remote_sidekiq_adapter.rb

代码语言:javascript
复制
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

现在我可以使用适配器对事件进行排队:

代码语言:javascript
复制
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

现在,我可以使用普通的ActiveJob api对作业进行排队。无论什么应用程序将其从队列中读取出来,都需要有一个匹配的RemoteJob来执行该操作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14741700

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档