浩文 联系 关于本站 登录 注册
  • 关注新浪微博:
  • 关注微信公众号:

Sidekiq Github wiki 中文文档

FAQ

commit信息:Note correct usage of redis.exists? on Redis >= 4.2.0 to avoid deprecations | 提交者:bf4 | 提交时间:2020-08-21 | 版本:a4a8eea

经常问的问题

sidekiq 与 resque 和 delayed_job相比较

这三个工具提供相同的功能--执行后台作业,但是下面是一些它们之间的不同:

  1. Delayed Job 通过单线程程序来使用SQL数据库作为储存,并且单线程执行作业。它的设置比较简单,但是性能和伸缩性不好。我将不会把delay_job用于每天处理100,000多个作业的系统。
  2. Resque 通过单线程来使用Redis来储存和处理信息。使用Redis使它的配置相比于delayed_job来说要复杂一些,但是,对用作队列来说,Redis比SQL数据库更好。使用单线程意味着,同时执行20个作业就需要20个进程,那将消耗许多内存。
  3. Sidekiq 通过多线程使用Redis来储存和处理作业。它像resque一样可以轻松配置,但是在处理速度方面更高效。你的worker代码必须是线程安全的。

我能期望sidekiq有什么样的性能表现?

那是变化无常的性能,不能给一个简单的回答。 大多数服务端作业消耗的时间主要受I/O时间影响,当大量的I/O占用时间发生时Sidekiq将会shines。Sidekiq 程序相比 Resque或者Delayed::Job程序,性能会有一个数量级的提升或者更多。

我知道的一个大客户使用Sidekiq每分钟处理大约500,000个作业,另一个客户报告使用多个Redis分片最高每秒处理50,000个作业。注意在专业的硬件上,Redis应该能支持每秒5000到20,000个作业, 具体取决你所使用的功能。如果还需要更多负载,你需要分片你的应用来使用多个Redis实例,或者使用多个独立的应用程序。

一个忙碌的Redis (redis-cli info的输出):

redis_version:4.0.9
redis_mode:standalone
os:Linux 4.15.0-1009-aws x86_64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:7.4.0
uptime_in_seconds:3279660
uptime_in_days:37
hz:10

# Clients
connected_clients:3005
blocked_clients:125

# Memory
used_memory:396704632
used_memory_human:378.33M
used_memory_rss:738783232
used_memory_rss_human:704.56M
used_memory_peak:8213446064
used_memory_peak_human:7.65G
used_memory_peak_perc:4.83%
used_memory_overhead:105619133
used_memory_startup:782448
used_memory_dataset:291085499
used_memory_dataset_perc:73.52%
total_system_memory:32655642624
total_system_memory_human:30.41G
maxmemory:26214400000
maxmemory_human:24.41G
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.86
mem_allocator:jemalloc-3.6.0

# Stats
total_connections_received:577443
total_commands_processed:163849263454
instantaneous_ops_per_sec:45476
total_net_input_bytes:20173258216040
total_net_output_bytes:24490441661513
instantaneous_input_kbps:6749.39
instantaneous_output_kbps:7769.90
rejected_connections:0
keyspace_hits:10854195921
keyspace_misses:3763186978
pubsub_channels:1
pubsub_patterns:331
latest_fork_usec:20204

# Keyspace
db0:keys=266128,expires=261259,avg_ttl=153283764

如果Sidekiq支持{MongoDB, postgresql, mysql, SQS, ...}来实现持久性,那会很棒吗?

并不是的。 Redis为我提供了一套有效的数据结构,以在其之上构建功能。我需要将这些结构抽象为一套API,为每个系统编写适配器,在各种版本上进行测试,然后记录每个系统的功能和性能限制。我的用户中只有少部分可以避开Redis。Sidekiq使用Redis提供的所有数据结构:lists, sorted sets, hashes。

If you want a queueing system that uses X, use a queuing system that uses X! Sidekiq的宗旨是简单并且高效。Redis能够两者都满足,两者都不是抽象化数据储存。

为什么在使用Sidekiq时我看到了很多“Can't find ModelName with ID=12345”的相关错误?

客户端在一个事务内创建模型实例,然后给Sidekiq推送一个作业。Sidekiq将在该事务最终完成之前去试着执行你的作业。请使用Rails's after_commit :on => :create钩子,或者把该作业移出事务块。

我可以串行处理特定的队列吗?

从sidekiq的设计方面来看,你做不到。Sidekiq被设计为异步处理作业,这些作业可以被独立的完成,并且作业之间相互独立不关联。作业将会按推送的顺序从Redis中弹出,但是那不能保证作业#1完成执行后再执行作业#2。

如果你需要串行执行,你应该调研能给这种类型保证的其它系统。

注意,你可以创建一个Sidekiq程序,专用于使用单个worker处理一个队列。它可以给你串行执行程序,但是它只是一个hack解决方案。

再提醒一下,你可以使用Sidekiq的第三方扩展来达到该目标。

当我进入Sidekiq的Web UI时,CSS/JS/IMG资源为什么不能正确加载?

静态资源服务issue

始终是一个web服务配置issue。为了伺服这些静态资源,你的web服务可能配置了特殊的规则,如果资源不在文件系统中,它将不能给Rails转交该请求,这会造成404响应。 例如:

location ~ /assets/  {
  try_files $uri;
}

这样,任何能匹配/assets/的uri都将搜索文件系统,对你的Rails静态资源来说,那可以很好的工作,像是{Rails.root}/public/assets中的/assets/application.css静态资源,但是它也会匹配Sidekiq的静态资源/sidekiq/assets/的请求。由于Sidekiq的静态资源存在于.gem文件中,文件系统查找检测会失败,然后返回一个404响应。下面是一个该issus的修复方案例子:

location ~ ^/assets/  {
  try_files $uri;
}

使用^来限制匹配域只到顶级资源。

X-SendFile的issue

Rack 使用 X-SendFile header来伺服 Sidekiq在gem之外的web UI静态资源。由于gem安装通常位于部署项目根目录之外,同时,由于一些安全因素,一些web服务默认不能伺服项目根目录之外的文件,你或许需要调整你的web服务配置。

例如,使用Apache时,你需要添加以下代码:

XSendFile on
XSendFilePath /mnt/my_project-production/shared/bundle/jruby/1.9/gems/sidekiq-2.5.3/web/assets/javascripts/vendor/

注意:伴随每次gem更新,该静态资源路径将会改变,因此每次更新安装的Sidekiq或者使用一个自动更新工具更新时,要记着更新服务配置。

Heroku中的X-SendFile

如果应用部署在Heroku上,并且在Sidekiq Web UI中有CSS/Images加载issues,你需要在production.rb环境配置文件中更新以下代码。

config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

Heroku 使用Nginx来server up 静态资源,因此为了正确伺服Sidekiq资源文件,它需要特定的x_sendfile_header。

我该怎样不使用Ruby把一个作业推送给Sidekiq?

如果你正在集成两个不同的系统,或许你不想使用Sidekiq::Client API启动一个作业。Sidekiq的消息格式是简单并且稳定的:只是一个JSON格式的Hash。这里有一个在Ruby中达到该目的的基本方法;你可以把下面代码转化为你选择的语言。必须生成一个唯一的标识来作为Job ID。

require 'securerandom'
require 'json'

redis = Redis.new(:url => 'redis://hostname:port/db')
msg = { "class" => 'MyWorker',
        "queue" => 'default',
        "args" => [1, 2, 3],
        'retry' => true,
        'jid' => SecureRandom.hex(12),
        'created_at' => Time.now.to_f,
        'enqueued_at' => Time.now.to_f }
redis.lpush("queue:default", JSON.dump(msg))

为了使队列在"Queues"选项卡中展示并且使"Enqueued"正确计数,你必须也把该队列的名称添加到queues集合:

redis.sadd("queues", "default")

这只是最基本的设置。添加命名空间支持或者ActiveJob支持是更复杂。

当一个作业完成时,我该如何tell?

有两个选项:

  1. 使用第三方插件来追踪给定作业的状态;请参考 [[Related Projects]] 页面。如果你想把作业的状态显现在你的应用UI上,请让你的页面每隔几秒轮询一次服务器,然后借助这些插件之一的功能来送回JSON格式的状态。

  2. Sidekiq Pro的[[Batches]]功能在一个或多个作业完成时可以在你的代码中触发一个回调。

当Sidekiq重启时,需要长时间运行的作业会怎样?

默认,Sidekiq 6+关闭时会给workers 25秒时间。(6以下版本的Sidekiq,请在运行时带上-t 25参数。)这是精心设计的,因为Heroku和AWS ECS 在杀死一个程序之前会给它30秒时间。在25秒之后,任何剩余的仍在进度中的作业会被推送回Redis,这是为了在Sidekiq重启时,它们可以被立即重新运行。记着,Sidekiq将至少运行你的作业一次,因此这些作业需要成为幂等的。This is one example of how a job can be run twice.

Sidekiq企业版支持 rolling restarts,有了该功能,Sidekiq为了作业的完成会等待必要的时间,如果需要,可能是数个小时。

我如何确保在一个给定的机器上处理一个作业?

例如:你有一个在文件系统里处理上传大文件的作业,但是你的Sidekiq运行在多个机器上。你如何确保上传到"app-1.example.com"的文件是被"app-1.example.com"中的Sidekiq所处理呢?

这很简单,请使用hostname-specific队列。运行Sidekiq时带上-q `hostname` -q default参数,这样Sidekiq将监听当前主机名的队列。

另一种方案,告诉每个Sidekiq进程监听一个以机器主机名命名的队列。在你的config/sidekiq.yml中做以下设置:

---
:verbose: false
:concurrency: 25
:queues:
  - <%= `hostname`.strip %>

Sidekiq通过ERB可以自动运行YAML文件,因此你可以轻松地动态添加队列。

在worker中配置作业到hostname-specific队列:sidekiq_options :queue => Socket.gethostname。只要确保hostname的返回值与Socket.gethostname的值相同。

我该如何取消一个Sidekiq 作业?

Sidekiq不提供该功能;让应用来做这件事是更安全也更好。你应该像下面一样实现一些功能:

class MyWorker
  include Sidekiq::Worker

  def perform(args)
    return if cancelled?
    # do stuff
  end

  def cancelled?
    Sidekiq.redis {|c| c.exists("cancelled-#{jid}") } # Use c.exists? on Redis >= 4.2.0
  end

  def self.cancel!(jid)
    Sidekiq.redis {|c| c.setex("cancelled-#{jid}", 86400, 1) }
  end
end

我该如何安全地重命名一个Worker?

如果在Redis中还有一些作业,重命名一个Worder是不安全的,因为worker的名被序列化到了作业负载中。下面是一个怪异的安全地重命名 技巧,对于该技巧,它们不想让你知道:

class MyNewWorker
  ...
end
# XXX Delete this alias in a few weeks when old jobs are safely gone
MyOldWorker = MyNewWorker

那是如此简单。

该如何计算Sidekiq使用的Redis的链接数?

web_connections = (web_dynos * (client_conn * web_threads))
concurrency  = (max_connections - web_connections - (redis_reserved * worker_dynos)) / worker_dynos
server_connections  = concurrency + redis_reserved

Source, tool, additionnal resource

上一篇: 信号 下一篇: 测试