【问题标题】:Detect action mailer delivery failures in after_action callbacks在 after_action 回调中检测动作邮件传递失败
【发布时间】:2016-07-08 12:29:33
【问题描述】:

我在我的邮件中使用after_action 回调来记录该电子邮件已发送。电子邮件通过延迟作业发送。这有效,除非我们无法访问远程服务器 - 在这种情况下,电子邮件不会发送,但我们会记录它。延迟作业稍后重试电子邮件,并成功发送,但我们记录了已发送两封电子邮件。

看起来像这样:

class UserMailer < ActionMailer::Base

  after_action :record_email

  def record_email
   Rails.logger.info("XYZZY: Recording Email")
   @user.emails.create!
  end

  def spam!(user) 
   @user = user
   Rails.logger.info("XYZZY: Sending spam!")
   m = mail(to: user.email, subject: 'SPAM!')
   Rails.logger.info("XYZZY: mail method finished")
   m
  end
end

我这样称呼这段代码(使用delayed job performable mailer):

UserMailer.delay.spam!( User.find(1))

当我在调试器中逐步执行此操作时,似乎我的 after_action 方法在邮件传递之前被调用。

[Job:104580969] XYZZY: Sending spam!
[Job:104580969] XYZZY: mail method finished
[Job:104580969] XYZZY: Recording Email
Job UserMailer.app_registration_welcome (id=104580969) FAILED (3 prior attempts) with Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 1025

如何在我的邮件方法中捕获网络错误并记录电子邮件尝试失败或什么都不做?我正在使用 Rails 4.2.4。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 actionmailer delayed-job


    【解决方案1】:

    这是我想出的,我希望有更好的方法。

    我使用了邮件传递回调:

    delivery_callback.rb

    class DeliveryCallback
      def delivered_email(mail)
        data = mail.instance_variable_get(:@_callback_data)
        unless data.nil?
          data[:user].email.create!
        end
      end
    end
    

    config/initializes/mail.rb

    Mail.register_observer( DeliveryCallback.new )
    

    我替换了我的 record_email 方法:

    class UserMailer < ActionMailer::Base
    
      after_action :record_email
    
      def record_email
        @_message.instance_variable_set(:@_callback_data, {:user => user}) 
      end
    end
    

    这似乎可行,如果远程服务器不可用,则不会调用 Delivered_email 回调。

    有没有更好的办法!?!?

    【讨论】:

    【解决方案2】:

    您显示的调试消息非常有意义 - 邮件操作会立即完成,因为邮件操作本身是异步,由延迟作业在完全不同的过程中处理。因此,邮件程序类本身无法知道邮件操作是如何完成的。

    我认为您需要的是实现 Delayed job hooks。不过,您必须重写您的邮件和发送电子邮件的电话。

    我尚未对其进行全面测试,但以下几行应该可以工作:

    class MailerJob
    
      def initialize(mailer_class, mailer_action, recipient, *params)
        @mailer_class = mailer_class
        @mailer_action = mailer_action
        @recipient = recipient
        @params = params
      end
    
      def perform
        @mailer_class.send(@mailer_action, @recipient, *@params)
      end
    
      def success(job)
        Rails.logger.debug "recording email!"
        @recipient.emails.create!
      end
    
      def failure(job)
        Rails.logger.debug "sending email to #{@recipient.email} failed!"
      end
    
    end
    

    MailerJob 是一个 custom job 由延迟作业运行。我试图使其尽可能通用,因此它接受邮件程序类、邮件程序操作、收件人(通常是用户)和其他可选参数。它还要求recipient 具有emails 关联。

    该作业定义了两个挂钩:success,当邮件操作成功时,会在数据库中创建email 记录,另一个用于记录失败。实际发送是在perform 方法中完成的。请注意,在其中没有使用 delayed 方法,因为整个作业在被调用时已经在后台延迟作业队列中排队。

    要使用此自定义作业发送邮件,您必须将其排入延迟作业,例如:

    Delayed::Job.enqueue MailerJob.new(UserMailer, :spam!, User.find(1))
    

    【讨论】:

    • 我的同事建议这样做。我认为这是个好主意,但我会扩展 Delayed::PerformableMailer (github.com/collectiveidea/delayed_job/blob/v4.1.1/lib/delayed/…) 的功能。我认为您对异步邮件操作是错误的。你是对的,我从那里调用 UserMailer.delay.spam! mail 方法稍后发生,但发送电子邮件的实际工作在延迟工作中是同步的。
    • 我的意思是“异步”,因为邮件操作不会等待实际邮件的结果(通过延迟作业完成)。我很抱歉,但我不太明白你对钩子方法有什么问题?从source 看来,PerformableMailer 内部似乎只不过是一个类,它以与我上面的回答几乎相同的方式将邮件工作排入队列。
    【解决方案3】:

    尝试以下方法:

    class UserMailer < ActionMailer::Base
    
      # after_action :record_email
    
      def record_email
       Rails.logger.info("XYZZY: Recording Email")
       @user.emails.create!
      end
    
      def spam!(user)
        begin 
          @user = user
          Rails.logger.info("XYZZY: Sending spam!")
          m = mail(to: user.email, subject: 'SPAM!')
          Rails.logger.info("XYZZY: mail method finished")
          m
        rescue Errno::ECONNREFUSED
          record_email  
        end
      end
    end
    

    【讨论】:

    • 那行不通。它与延迟作业如何处理电子邮件有关。它调用您的邮件方法,然后在其上调用交付(或交付_now)。因此,实际交付是在垃圾邮件之外执行的!方法
    • 我还想要一些可以工作的东西,而不必记住在每个邮件方法上实现它或更改我现有的所有邮件方法(大约有 30 个)
    猜你喜欢
    • 2011-10-18
    • 2011-09-19
    • 2014-07-21
    • 1970-01-01
    • 2022-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多