我在rails应用中使用sidekiq以异步方式发送一些电子邮件。即使Redis服务器未运行,我如何确保执行代码(作业本身)。
CommentsWorker.perform_async(@user.id, @comment.id)
在评论工作者中,我正在提取用户和评论,然后发送电子邮件:
def perform(user_id, comment_id)
user = User.find(user_id)
comment = Comment.find(comment_id)
CommentMailer.new_comment(user, comment).deliver
end
如果我停止Redis服务器,我的应用程序会引发异常Redis::CannotConnectError
即使服务器停止,我仍然希望使用旧式同步代码发送该电子邮件。我尝试从该异常rescue
,但由于某种原因它不起作用。
答案 0 :(得分:5)
想出来。解决方案是测试redis连接并从异常中解救,但之前调用perform_async
。现在只有一个小问题就是必须等待连接超时,但我想我可以忍受这个。
redis_available = true
Sidekiq.redis do |connection|
begin
connection.info
rescue Redis::CannotConnectError
redis_available = false
end
end
if redis_available
CommentsWorker.perform_async(user.id, @comment.id, @award.id)
else
#sync code
user = User.find(user_id)
comment = Comment.find(comment_id)
CommentMailer.new_comment(user, comment).deliver
end
答案 1 :(得分:2)
I know this has already been answered and I liked @mihai's approach, but we wanted to reuse the code elsewhere in our application for multiple workers. Also, we wanted to make it more generic to work with any worker.
We decided to extend Sidekiq::Worker
with an additional method perform_async_with_failover
defined below:
module Sidekiq
module Worker
module ClassMethods
def perform_async_with_failover(*args)
redis_available = true
Sidekiq.redis do |connection|
begin
connection.info
rescue Redis::CannotConnectError
redis_available = false
end
end
if redis_available
# process the job asynchronously
perform_async(*args)
else
# otherwise, instantiate and perform synchronously
self.new.perform(*args)
end
end
end
end
end
答案 2 :(得分:1)
改进Kyle的解决方案,因为我想使用connection.info
是为了查看是否有可用的连接。但我的理解是,info
会在redis联机时发送额外的命令,这是不必要的。我只想从perform_async
获取异常:
module Sidekiq
module Worker
module ClassMethods
def perform_async_with_failover(*args)
begin
# process the job asynchronously
perform_async(*args)
rescue Redis::CannotConnectError => e
# otherwise, instantiate and perform synchronously
self.new.perform(*args)
end
end
end
end
end
答案 3 :(得分:1)
实现@mihai的答案,我最终创建了一个服务来封装“优雅地发送电子邮件”的动作。
调用类的示例:
message = MyMailer.order_confirmation(email_arguments)
GracefullyDeliverEmail.call(message)
课程:
class GracefullyDeliverEmail
###
# Attempts to queue email for async sending but fails
# gracefully to delivering it immediately.
#
# @param context.message {ActionMailer::MessageDelivery}
###
def self.call(message)
validate!(message)
if redis_available?
message.deliver_later(wait: 2.mins)
else
message.deliver_now # Fallback to inline delivery
end
end
# == Private Methods ======================================================
private
# https://stackoverflow.com/questions/15993080/sidekiq-fall-back-to-standard-sync-ruby-code-when-redis-server-is-not-running/42247913#42247913
def redis_available?
redis_available = true
Sidekiq.redis do |connection|
begin
connection.info
rescue Redis::CannotConnectError
redis_available = false
end
end
redis_available
end
def validate!(message)
if !message.is_a?(ActionMailer::MessageDelivery)
raise "message must be of class ActionMailer::MessageDelivery"
end
end
end
答案 4 :(得分:0)
<强>编辑强>:
你可能想看看sidekiq error handling section
答案 5 :(得分:0)
您应该检查是否存在活动的redis客户端,例如:
def perform(user_id, comment_id)
user = User.find(user_id)
comment = Comment.find(comment_id)
redis_info = Sidekiq.redis { |conn| conn.info }
CommentMailer.new_comment(user, comment).deliver
rescue Redis::CannotConnectError
CommentMailer.new_comment(user, comment)
end
应该这样做。