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相比较
这三个工具提供相同的功能--执行后台作业,但是下面是一些它们之间的不同:
- Delayed Job 通过单线程程序来使用SQL数据库作为储存,并且单线程执行作业。它的设置比较简单,但是性能和伸缩性不好。我将不会把delay_job用于每天处理100,000多个作业的系统。
- Resque 通过单线程来使用Redis来储存和处理信息。使用Redis使它的配置相比于delayed_job来说要复杂一些,但是,对用作队列来说,Redis比SQL数据库更好。使用单线程意味着,同时执行20个作业就需要20个进程,那将消耗许多内存。
- 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?
有两个选项:
使用第三方插件来追踪给定作业的状态;请参考 [[Related Projects]] 页面。如果你想把作业的状态显现在你的应用UI上,请让你的页面每隔几秒轮询一次服务器,然后借助这些插件之一的功能来送回JSON格式的状态。
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