2011-03-15 34 views
2

Grails使用Spring的mailService。該服務是同步的,這意味着如果SMTP暫時關閉,應用程序功能會受到嚴重影響(HTTP 500)。Grails:當SMTP服務器暫時關閉時如何緩衝出站電子郵件?

我想從SMTP服務器分離應用程序。

該計劃是將準備發送的電子郵件保存到出站隊列中,並通過定時器發送,並重試。對於我自己的代碼,當我直接調用mailService時,它是相當微不足道的 - 創建一個包裝服務並調用它。但是,我的應用程序使用的一些插件(例如,EmailConfirmation插件)使用相同的mailService,並且仍然失敗,例如有效阻止註冊過程。

我想知道如何替換/包裝mailService的定義,使所有代碼,我自己的和插件透明地使用我自己的服務?

I.e.

  • 插件代碼注入MailService的
  • 但不是春默認MailService的我自己的代碼注入
  • 當插件發送電子郵件的電子郵件對象保存到數據庫,而不是
  • 在計時器工作醒來,獲取下N個電子郵件並嘗試發送它們

任何想法如何解決這個問題?

P.S.我知道關於AsynchronousMail插件。不幸的是,它的服務必須被明確地調用,即它不是郵件服務的直接替換。

回答

0

好的,畢竟它並不那麼辛苦。簡單的步驟,第一:

第一步:準備數據庫表來存放待處理的電子郵件記錄:

class PendingEmail { 
    Date sentAt = new Date() 
    String fileName 

    static constraints = { 
     sentAt nullable: false 
     fileName nullable: false, blank:false 
    } 
} 

第二步:創建一個週期性的任務發送掛起的電子郵件。 (!和它的配置) - 注意:mailSender注射是原創的Grails郵件插件的一部分,因此發送通過郵件插件製作:

import javax.mail.internet.MimeMessage 

class BackgroundEmailSenderJob { 

    def concurrent = false 
    def mailSender 

    static triggers = { 
     simple startDelay:15000l, repeatInterval: 30000l, name: "Background Email Sender" 
    } 

    def execute(context){ 
     log.debug("sending pending emails via ${mailSender}") 

     // 100 at a time only 
     PendingEmail.list(max:100,sort:"sentAt",order:"asc").each { pe -> 

      // FIXME: do in transaction 
      try { 
       log.info("email ${pe.id} is to be sent") 

       // try to send 
       MimeMessage mm = mailSender.createMimeMessage(new FileInputStream(pe.fileName)) 
       mailSender.send(mm) 

       // delete message 
       log.info("email ${pe.id} has been sent, deleting the record") 
       pe.delete(flush:true) 

       // delete file too 
       new File(pe.fileName).delete(); 
      } catch(Exception ex) { 
       log.error(ex); 
      } 
     } 
    } 
} 

第三步:創建一個下拉更換MailService的那可以被任何Grails代碼使用,包括插件。注意mmbf注入:這是來自Mail Plugin的mailMessageBuilderFactory。該服務使用出廠序列化進入封閉的呼叫到一個有效的MIME消息,然後將其保存到文件系統:

import java.io.File; 

import org.springframework.mail.MailMessage 
import org.springframework.mail.javamail.MimeMailMessage 

class MyMailService { 
    def mmbf 

    MailMessage sendMail(Closure callable) { 
     log.info("sending mail using ${mmbf}") 

     if (isDisabled()) { 
      log.warn("No mail is going to be sent; mailing disabled") 
      return 
     } 

     def messageBuilder = mmbf.createBuilder(mailConfig) 
     callable.delegate = messageBuilder 
     callable.resolveStrategy = Closure.DELEGATE_FIRST 
     callable.call() 
     def m = messageBuilder.finishMessage() 

     if(m instanceof MimeMailMessage) { 
      def fil = File.createTempFile("mail", ".mime") 
      log.debug("writing content to ${fil.name}") 
      m.mimeMessage.writeTo(new FileOutputStream(fil)) 

      def pe = new PendingEmail(fileName: fil.absolutePath) 
      assert pe.save(flush:true) 
      log.debug("message saved for sending later: id ${pe.id}") 
     } else { 
      throw new IllegalArgumentException("expected MIME") 
     } 
    } 

    def getMailConfig() { 
     org.codehaus.groovy.grails.commons.ConfigurationHolder.config.grails.mail 
    } 

    boolean isDisabled() { 
     mailConfig.disabled 
    } 
} 

第四步:與修改後的版本替換郵件插件的MailService的,與注射它工廠。 grails-app/conf/spring/resources.groovy

beans = { 
    mailService(MyMailService) { 
     mmbf = ref("mailMessageBuilderFactory") 
    } 
} 

完成!

從現在開始,任何使用/注入mailService的插件或Grails代碼都將獲得對MyMailService的引用。該服務將接收發送電子郵件的請求,但不會發送它,而是將其序列化到磁盤上,並將記錄保存到數據庫中。定期任務將每30秒加載一堆這樣的記錄,並嘗試使用原始郵件插件服務發送它們。

我測試了它,它似乎工作正常。我需要在這裏和那裏進行清理,在發送時添加事務範圍,使參數可配置等等,但骨架已經是可行的代碼。

希望能幫助別人。

+0

是的,我認爲代碼會幫助我,謝謝,因爲我將我的web應用程序部署到Linux主機環境。我可以問你,你爲SMTP服務器決定了什麼。你有沒有發現任何簡單和輕鬆..它在您的服務器上運行? – Ray 2011-08-24 02:13:43

+0

我開發的SMTP是在我的開發箱上運行的postfix; PROD中的SMTP是我的託管公司使用的。我不在乎他們運行的是什麼(我相信這是qmail,但可能會誤解)。 – 2011-08-25 14:51:21

2

一個簡單的解決方案是使用本地安裝的郵件服務器。還有像Postfix,Sendmail或Exim這樣的衆所周知的全面MTA,以及像http://packages.qa.debian.org/s/ssmtp.html這樣的輕量級替代品。

配置使用的MTA包將其所有電子郵件中繼到您的域的真實SMTP服務器。 Grails應用程序將簡單地使用127.0.0.1作爲SMTP主機。

這也有利於提高應用程序的響應時間,因爲電子郵件發送不再需要任何非本地IP通信。

+0

好點。除了當地的MTA可能會下降。最終目標是將應用程序與外部服務分離。 – 2011-03-15 12:39:15

+0

表示同意,但MTA主要是堅如磐石的,多年來我從未觀察到例如一個後綴下降。 – 2011-03-15 19:37:27

+0

這不僅僅是在每個環境中,開發人員都可以決定在哪裏安裝MTA。有時由於操作人員的錯誤或升級等原因,即使是堅如磐石的本地MTA也可能會停機。 – 2011-03-16 12:59:28

相關問題