我有一个rails应用程序可以从多个IMAP帐户中获取大量电子邮件。
2个问题:
我的代码:
class FetchMailsJobs
include Sidekiq::Worker
include Sidetiq::Schedulable
tiq { hourly.minute_of_hour(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55) }
def perform(last_occurrence, current_occurrence)
users = User.all
users.each do |user|
if user.imap_accounts.exists?
ImapJob.perform_async(user._id.to_s)
end
end
end
end
class ImapJob
include Sidekiq::Worker
def perform(user_id)
s = Redis::Semaphore.new("fetch_imap_mails_for_#{user_id}".to_sym, connection: "localhost")
if s.lock
user = User.where(_id: user_id).first
emails = ImapMails.receive_mails(user)
s.unlock
end
end
end
答案 0 :(得分:6)
Redis
子类并重载blpop
以接受-1
,以便非阻塞地使用lpop
。 redis-semaphore 在Redis::Semaphore#lock
中调用@redis.blpop
。虽然您可以重载lock
方法以使用@redis.lpop
,但更简单的方法是将Redis
的自定义实例传递给信号量。
将以下内容放入您的rails应用的lib
中,并在config/initializers/sidekiq.rb
中提出要求(或根据您的偏好执行以下操作,以便加载以下课程。)
class NonBlockingRedis < Redis
def blpop(key, timeout)
if timeout == -1
result = lpop(key)
return result if result.nil?
return [key, result]
else
super(key, timeout)
end
end
end
每当您致电Redis::Semaphore.new
时,请使用:redis
类的新实例传递NonBlockingRedis
密钥。
使用s.lock
作为参数调用-1
,以使用lpop
代替blpop
。
s = Redis::Semaphore.new("fetch_imap_mails_for_#{user_id}".to_sym, redis: NonBlockingRedis.new(connection: "localhost"))
if s.lock -1
user = User.where(_id: user_id).first
emails = ImapMails.receive_mails(user)
s.unlock
end
sidekiq_options retry: false
应该可以工作,请参阅下面的示例。在您的问题中,您没有指定哪个工作人员遇到了在重试队列中结束的作业的问题。由于FetchMailsJobs
结束了ImapJob
个ImapJob
个作业,因此前者中的例外情况可能会导致begin rescue ensure
重新排队。
使用您的信号量锁定,将您的工作包装在class FetchMailsJobs
include Sidekiq::Worker
include Sidetiq::Schedulable
sidekiq_options retry: false
tiq { hourly.minute_of_hour(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55) }
def perform(last_occurrence, current_occurrence)
users = User.all
users.each do |user|
if user.imap_accounts.exists?
ImapJob.perform_async(user._id.to_s)
end
end
end
end
class ImapJob
include Sidekiq::Worker
sidekiq_options retry: false
def perform(user_id)
s = Redis::Semaphore.new("fetch_imap_mails_for_#{user_id}".to_sym, redis: NonBlockingRedis.new(connection: "localhost"))
if s.lock - 1
begin
user = User.where(_id: user_id).first
emails = ImapMails.receive_mails(user)
rescue => e
# ignore; do nothing
ensure
s.unlock
end
end
end
end
块中也是一个好主意。
{{1}}
有关详细信息,请参阅sidekiq Advanced Options: workers。
答案 1 :(得分:1)
使用 redis-semaphore 并使用sidekiq-unique-jobs gem是不可能的?它似乎是一个很好的包含工具,可以完全满足您的需求。