码云 Gitee 在使用 Sidekiq 作为队列任务的时候,时不时遇到队列阻塞。这时候队列任务计划数 ( Scheduled ) 为个位数或 0,重试数 ( Retry ) 为个位数或 0,正在执行的任务数 ( Busy ) 为 0,而已经进入了队列的数目 ( Enqueued ) 一旦阻塞不执行,数量一直增加,达到几十 K,几百 K。正常的情况下,已经进入了队列的数目也就是个位数。检测了一下,sidekiq 相关的所有的 http 请求都已经加上了超时时间,阻塞还是会时不时发生。只能使用重启大法。无奈之下,@zoker 提出了根据这个数量来监控重启 Sidekiq 的想法。
参考 Sidekiq 的源码,照葫芦画瓢,写了一个监控的 ruby 脚本,放到 crontab 下两分钟执行一次。Enqueued 数量大于 1000 就重启。也可以放到 Zabbix 进行监控报警。
require 'redis'
require 'redis-namespace'
redis_connection = Redis.new
# namespace 根据需要改写
namespaced_redis = Redis::Namespace.new('resque:gitee', redis: redis_connection)
pipe1_res = namespaced_redis.pipelined do
namespaced_redis.zcard('schedule'.freeze)
namespaced_redis.zcard('retry'.freeze)
namespaced_redis.smembers('processes'.freeze)
namespaced_redis.smembers('queues'.freeze)
end
pipe2_res = namespaced_redis.pipelined do
pipe1_res[2].each { |key| namespaced_redis.hget(key, 'busy'.freeze) }
pipe1_res[3].each { |queue| namespaced_redis.llen("queue:#{queue}") }
end
s = pipe1_res[2].size
workers_size = pipe2_res[0...s].map(&:to_i).inject(0, &:+)
enqueued = pipe2_res[s..-1].map(&:to_i).inject(0, &:+)
stats = {
scheduled_size: pipe1_res[0],
retry_size: pipe1_res[1],
busy_size: workers_size,
enqueued_size: enqueued
}
stats.each do |stat, value|
puts "#{stat}: #{value}"
end
# 根据需要重启
`bundle exec rake sidekiq:restart RAILS_ENV=production` if stats[:enqueued_size] > 1000