跟踪一下include了Resque::Mailer的一个ActionMailer::Base子类Notifier的运作

[2] pry(main)> binding.trace_tree(htmp: 'resque_mailer'){ Notifier.send_an_email('a@b.com', 'subject', 'content').deliver }
  Rendered notifier/send_an_email.html.erb within layouts/notifier (1.7ms)

Notifier#send_an_email: processed outbound mail in 70.8ms
=> true

得调用栈如下:

20190620_111819_204_resque_mailer.html

基本流程如下:


ActionMailer::Base的子类只需定义实例方法,调用时按类方法来调,然后进入method_missing,如果方法action_methods计算出的controller实例方法包含该方法名,则将实际类名、方法名和参数包装成一个MessageDecoy

module Resque
  module Mailer
    class << self
     def included(base)
       base.extend(ClassMethods)
     end
   end

   module ClassMethods
     def method_missing(method_name, *args)
       if action_methods.include?(method_name.to_s)
         MessageDecoy.new(self, method_name, *args)
       else
         super
       end
     end

MessageDecoy的deliver就是通过Resque塞到redis里

def deliver
  return deliver! if environment_excluded?

  if @mailer_class.deliver?
    begin
      resque.enqueue(@mailer_class, @method_name, @serialized_args)
    rescue Errno::ECONNREFUSED, Redis::CannotConnectError
      logger.error "Unable to connect to Redis; falling back to synchronous mail delivery" if logger
      deliver!
    end
  end
end

注意塞进resque的参数应是能准确序列化和反序列化的