2013-01-05 80 views
2

目前我一直在使用PHP和Zend Framework來發送與SMTP和Gmail的SMTP服務器的郵件太慢。這是因爲我有一個Google Apps域設置來處理我所有的電子郵件,所以看起來我的電子郵件來自[email protected]在PHP發送電子郵件和使用谷歌Apps郵件的SMTP是

現在,當用戶註冊或做一些事情需要我的Web服務器發送電子郵件給他們這個可能需要5秒左右的時間在PHP端發起的SMTP連接,併發送電子郵件給他們。這一過程將是這樣的:

  1. 用戶完成註冊表格
  2. 用戶提交表單
  3. PHP腳本將信息保存到數據庫中,並將它們發送電子郵件(5-10秒)
  4. 用戶重定向到另一頁

現在,當他們剛剛提交表單時,我不能從我的Web服務器有一個巨大的延遲,而用戶坐在那裏10秒看起來像加載。基本上發送電子郵件的過程是單線程的,直到它完成發送是5-10秒,有時20秒是否有與電子郵件地址錯誤之間的任何電子郵件服務器不能做任何事情,所以這是太長了。

如何周圍的人這樣的問題通常會得到?

本來我試了幾件事情:

  1. 使用像SwiftMailer另一個庫。仍是同樣的問題。我認爲這是需要額外時間的SMTP/TLS握手。

  2. 將電子郵件數據存儲在數據庫中,然後使用fsockopen向另一個頁面發起異步請求,然後從另一個頁面獲取電子郵件數據併發送電子郵件。同時我的PHP腳本可以繼續同步,用戶將在表單提交後立即重定向到下一頁。問題在於無法確認電子郵件是否已發送,或者是否失敗。我可以在數據庫表上設置一個標誌,但我想提供即時反饋給用戶他們的電子郵件已發送。這意味着在下一頁上,在10秒後向另一個頁面發出Ajax請求,該頁面將檢查數據庫表上的標誌以查看它是否被髮送,然後在頁面上向用戶顯示響應。如果他們輸入了錯誤的電子郵件並且無法發送電子郵件,這將不起作用,因爲GMail的響應有時需要20秒才能發生錯誤,那麼數據庫標誌將不會被設置。

  3. 我想出的另一個選擇是將電子郵件數據放入$ _SESSION數組中,所以在提交頁面並且用戶位於重定向頁面之後,它會將ajax請求發送到另一個頁面,會話數據併發送電子郵件,然後將回復發送回用戶所在的頁面,並告訴他們電子郵件已發送。這工作得很好,但我覺得這可能不是最好的方法。

所以我想知道什麼是最好的做法呢?對於這種事情,GMail太慢了,因爲它必須進行TLS握手?我應該將所有電子郵件排入數據庫並每隔幾分鐘運行一次CRON作業發送它們,然後將它們從數據庫中刪除一次發送?問題在於沒有即時反饋給用戶他們的電子郵件已正確發送。或者我不需要爲此煩惱?他們要麼收到郵件,要麼沒有。

回答

1

您是否考慮過使用主機的電子郵件smtp服務器並手動設置發件人地址?這通常對我有用。如果這不是一個選項,我的第一反應將是選項3 - 這與Google郵件在您的web ui中選擇延遲發送選項時的作用類似。

+0

你的意思是用我的主機的電子郵件的SMTP服務器?我正在使用自己的VPS,所以我必須自己設置所有的權利? – zuallauz

+0

是的,但並不難,速度也更快。 – 2013-01-05 23:50:51

+0

@Dagon,你有什麼指南可以在Linux上設置它嗎?上次我看着它,我迷路了。這是否正確...我的Web服務器會發送電子郵件(也許使用PHP的send()方法)由我的郵件服務器在同一臺Web服務器上處理。該郵件服務器然後使用Google Apps SMTP將該電子郵件發送給用戶,還是使用其他一些我必須設置的電子郵件地址? – zuallauz

1

如何編寫一個應用程序腳本,並將其用作web應用程序來處理電子郵件。您可以使用帶參數的HTTP Get使應用程序腳本使用基本郵件服務發送電子郵件。 MailApp.sendEmail()

我是一個自學成才的應用程序腳本,所以我只是在這裏進行推測,但理論上它應該可以工作。我認爲它會看起來像這樣。

您應該可以通過webApp url在e.parameter末尾傳遞鍵。

function doGet(e){ 
var userEmail = e.parameter.email; 

var body = 'message here';//could even pass params for the body also. 

MailApp.sendEmail(userEmail, 'Subject', 'body', {noReply: true}); 
} 

我們所有的東西是建立在應用程序腳本,我有幾十個電子郵件通知的走出去,並在Google Apps腳本的瞬間。

希望這會有所幫助。

1

我有這個確切的問題,我實際上做的是,我只是在數據庫中排隊他們,我有一個cronjob運行每分鐘發送數據庫中排隊的電子郵件。

順便說一下,許多中型網站這樣做,並告訴你,你應該在(5分鐘)收到一封電子郵件或短........等

以dB爲單位節約的好處是你可以添加一個額外的列來指示它是否被髮送,如果它沒有在下一個cronjob中發送則重試。


更新用於鎖定的cronjob:

請注意,這是一個順序邏輯僅注意到步驟1,2,和圖4是大約的Cron#1

1-Cron #1 runs at 12:05, reads the locking table and finds it empty so it creates a db record in a pre-created table "locking" with 1 field "cronlock": holding the value 12345678910, which is the current time stamp. 

2-Cron #1 at 12:06 (Still sending emails, but it completed 6 emails so far) so it updates the record of locking again with the current time-stamp to extend the locking period. 

3-Cron #2 runs at 12:07, reads the locking table and find a value, compares this value with the current timestamp if the difference is less than 120 seconds, then it terminates itself. 

4-Cron #1 finishes sending some emails at 12:08, it deletes the locking record and quits.** 

5-Cron #3 runs at 12:09 and finds no locking record so it creates a new one and starts sending.... and so on. 
+0

比方說,我遇到了一段繁忙的網站流量,並有50多封電子郵件在數據庫中排隊等候發送。發送每封電子郵件需要大約5-10秒。可以想象,下一個cronjob可能會再次運行,並開始發送相同的電子郵件列表,因爲另一個cronjob尚未完成。然後客戶會收到2封很糟糕的郵件。你將如何解決併發問題?也許在表格中的行上設置一個標誌,試圖發送那個標誌?所以如果下一個cronjob出現了,它會忽略這一行並繼續到另一個? – zuallauz

+1

添加帶有時間戳的鎖定字段進行鎖定,並在發送時進行更新,如果不是,則釋放鎖定 – Shehabix

+0

是的,很酷,應該可以工作,謝謝。您是否將發送的電子郵件無限期地保存在數據庫中?或者你在某個時候清除它們?我認爲一旦發送後我可以將其清除,因爲如果我登錄到Google Apps並查看是否真的需要,我發送郵件的地址仍然會有一個副本。 – zuallauz

1

其實我使用相同的組件來發送電子郵件。但我在後臺使用Zend_Queue發送電子郵件,在這種情況下,訪問者不必等待。這是我如何完成這項任務(Zend_QueueBootstap.php初始化):

1.Creating工作

class My_Job_SendEmail extends My_Job 
{ 
    /** 
    * Message subject: 
    * 
    * @var string|null 
    */ 
    protected $_subject = null; 

    /** 
    * Message body: 
    * @var string|null 
    */ 
    protected $_message = null; 

    /** 
    * Message To: 
    * @var string|null 
    */ 
    protected $_to = null; 

    public function __construct(Zend_Queue $queue, array $options = null) 
    { 
     if (is_array($options)) { 
      $this->setOptions($options); 
     } 

     parent::__construct($queue); 
    } 

    public function job() 
    { 
     $mail = new Zend_Mail(); 
     $mail->addTo($this->_to); 
     $mail->setSubject($this->_subject); 
     $mail->setBody($this->_message); 
     return $mail->send(); 
    } 
} 

2.Source My_Job

3.Adding作業到的隊列(可能在您服務的某個地方)

$backgroundJob = new My_Job_SendEmail(Zend_Registry::get('queue'), array(
    'to'  => $to, 
    'bcc'  => $bcc, 
    'subject' => $subject, 
    'message' => $message, 
)); 

$backgroundJob->execute(); 

4.查看this回答,作爲背景腳本的例子。

5。添加背景腳本到的cronjob

*/1 * * * * php /path/to/project/background/emailQueue.php