2015-10-07 84 views
3

我有一個帶有電子郵件的MySQL表將被髮送出去。避免競爭條件,但仍然能夠回滾

在每個頁面加載時,我檢查是否有任何未發送的電子郵件,接收它們中的一些併發送它們。

爲了防止兩個同時發生的頁面加載從發出相同的電子郵件,我想在做這樣的事情的:

$pdo = new PDO(...); 

// Start blocking other page loads 
$pdo->beginTransaction(); 
$stmt = $pdo->query("SELECT id, recipient, subject, body 
    FROM emails WHERE sent = 0 LIMIT 1 FOR UPDATE"); 

$mail = $stmt->fetch(); 

if(false !== $mail) 
    $pdo->exec("UPDATE emails SET sent = 1 WHERE id = $mail['id']"); 

// End blocking other page loads 
$pdo->commit(); 

if(false !== $mail) { 
    // Send e-mail 
} 

但如果執行後提交中止什麼,但電子前郵件成功發送?電子郵件將作爲發送的市場,但實際上並未發送。當然,我可以等到發送電子郵件之後再進行提交,但這會導致更長的阻止時間。我通過SMTP發送電子郵件,發送單個電子郵件大約需要10秒。

你對如何解決這個問題有什麼想法嗎?一種選擇可能是檢測表是否被鎖定,然後跳過整個步驟。這可能嗎?

+2

你是否在頁面加載中檢查了這個,因爲你不能在你的webspace上使用cronjobs? –

回答

0

你可以把它標記爲未決在發送前和發送後,你必須把它。但是您仍然需要考慮在將其設置爲待定並將其設置爲發送之間發生的情況。

如果您不想發送更多「x」電子郵件,那麼在發送郵件之前,您可以考慮對標記爲的條目進行計數,並等待

類似的東西:

$pdo = new PDO(...); 

// Start blocking other page loads 
$pdo->beginTransaction(); 
$stmt = $pdo->query("SELECT id, recipient, subject, body 
    FROM emails WHERE status = 'queued' LIMIT 1 FOR UPDATE"); 

$mail = $stmt->fetch(); 

if(false !== $mail) 
    $pdo->exec("UPDATE emails SET status = 'pending' WHERE id = $mail['id']"); 

// End blocking other page loads 
$pdo->commit(); 

if(false !== $mail) { 
    // Send e-mail 
    if($successfull) { 
     $pdo->exec("UPDATE emails SET status = 'sended' WHERE id = $mail['id']"); 
    } else { 
     $pdo->exec("UPDATE emails SET status = 'failed' WHERE id = $mail['id']"); 
    } 
} 
0

回滾是一個問題

pendingsuccessfullfailed可以爲status領域是可能的值。

你必須檢查電子郵件傳送失敗,所以你跟蹤你實際交付的郵件(例如:10提供了100 sended),嘗試用Maildir工作並檢查新郵件的關鍵字,那麼這樣failure + [email protected]電子郵件更新數據庫相應。

使用大量的電子郵件發送。

  • 利用隊列,可能是一組電子郵件發出後最安全的門檻和「冷卻時間」。

  • 創建一個鎖文件,以避免種族,如果它存在,然後去睡覺別的開始發送

2

使用排隊系統本(Redis的,beanstalkd,RabbitMQ的,等等),如果你想要這個以任何方式進行縮放。

發送基於頁面加載郵件是從長遠來看,一個可怕的想法,因爲你不發送電子郵件異步,而是由很多放緩隨機用戶的頁面加載下來。

下面是一個例子:

乘坐Redis的隊列,併發布JSON字符串包括電子郵件ID從他們發送:

{"id":1, "job":"pending", "data": {"user": "foobar"}} 

做一個cronjob訂閱這個隊列,並連接到數據庫併發送這些ID的電子郵件。

如果有錯誤,您只需將作業更改爲"job":"errored"。在您的電子郵件任務的下一次計劃運行中,您將在那裏處理它。

這裏有很多的隊列庫,並且在頁面加載上執行異步任務是錯誤的方式。