通过cron作业发送电子邮件的实现

时间:2015-02-05 09:39:03

标签: php mysql email cron

考虑以下' pending_send_confirm'数据库中的表,用于存储我需要发送电子邮件的电子邮件地址(最终用户将通过该电子邮件确认订阅):

id| address |occupied|
0 |em1@s.com|0       |
1 |em2@s.com|0       |
2 |em3@s.com|0       |

当用户订阅(例如,我的简报)时,我想要显示一条消息,说明一切都很快就会好起来的。在显示消息之前,我不希望他等待服务器将确认电子邮件发送到他的地址。

这就是我设置每分钟运行一次cron作业以管理必须发送的电子邮件的原因。根据我提出的表格(如上所示),这是一些伪代码向每个地址发送电子邮件,1乘1:

//child_script.php
//fetch the entries from the database
while ($entry = $result->fetch_assoc()) {
    if ($entry['occupied']) {
        /*
         * Another instance of this script has 'occupied' this address, which
         * means that it is currently trying to send a confirmation email to
         * this address. So you know that another instance is working with this
         * email address, so skip it.
        */
        continue;
    }

    /*
     * Entry is not occupied, occupy it now in order to prevent future instances
     * of this script to attempt to send additional confirmation email to this address
     * while this instance of the script tries to send the confirmation email to this address.
     * occupied=1 means that an attempt to send the confirmation email is under the way
    */

    occupyEntry($entry['id']); //sets 'occupied' to 1

    if (sendConfirmationEmail($entry['address'])) {

        /*
         * Email was sent successfully, move the email address from the 'pending_send_confirm' to the 
         * 'pending_confirmation_from_user' table.
        */

        moveToConfirmPendingFromUserTable($entry['id']);
    } else {

        /*
         * Failed to send the email, unoccupy the entry so another instance of the script
         * can try again sometime in the future
        */

        unoccupyEntry($entry['id']); //sets 'occupied' to 0
    }


}

没有可读性评论的代码:

//child_script.php
while ($entry = $result->fetch_assoc()) {

    if ($entry['occupied']) {
        continue;
    }

    occupyEntry($entry['id']);

    if (sendConfirmationEmail($entry['address'])) {
        moveToConfirmPendingTable($entry['id']);
    } else {
        unoccupyEntry($entry['id']);
    }
}

这是否是一个可靠的解决方案,以防止发送重复的电子邮件?我担心脚本的2个实例可能会在同一时间发现"对于特定ID,$entry['occupied']为0,并尝试向该地址发送电子邮件。

另一个解决方案是使用flock(How can I ensure I have only one instance of a PHP script running via Apache?),以确保我的脚本只有一个实例正在运行。

但是我可以看到flock实现的许多问题。例如,如果我的脚本在调用fclose($fp)之前崩溃会发生什么?我的脚本的下一个实例是否能够继续,或者它会将其视为运行脚本的另一个实例(也就是flock函数将返回到脚本的新实例)?另一个问题是我的脚本一个接一个地发送电子邮件。这意味着如果我有100封电子邮件要发送并且我在3.5分钟内发送它们,那么下一个实例将在第一个实例STARTS后4分钟开始。这意味着如果订户选择订阅第一个实例开始的那一刻,那么他们将不得不等待4分钟以上才能收到他们的确认电子邮件。如果我允许脚本并行工作,那么电子邮件将以更快的速度发送。所以我希望能够拥有相同脚本的多个实例,同时发送电子邮件。

另一方面,如果我的脚本使用'占用'方法工作正常,我是否可以让另一个脚本管理将要启动的同时实例的数量?例如,我可以执行以下操作吗? :

//master_script.php

/*
 * Launch many instances of child_script.php.
 * If there are 901 to 1000 emails, then start 10 instances etc
*/

launch_n_instances( ceil(number_of_non_occupied_entries()/100.0) );

解决这个问题的正确方法是什么?

2 个答案:

答案 0 :(得分:0)

在另一个表中创建一个名为“cron_running”的字段或类似的字段,并在cron脚本的顶部将其设置为true。在脚本结束时将字段设置为false。这将允许您检查cron脚本是否仍在运行,如果它恰好重叠而不是继续。

答案 1 :(得分:0)

在阅读有关您的问题时,会出现很多问题:

你说:

  

我不希望他在显示消息之前等待服务器将确认电子邮件发送到他的地址。

首先,通过发送确认邮件添加的延迟应该非常短。

其次,简报订阅看起来很像电子邮件"对我来说。你怎么知道,如果你没有得到一些确认,"订阅就好了#34;

你提到的另一个问题实际上归结为如何记录某些状态 - 在这种情况下"发送的电子邮件" - 用于数据库中的多个进程。鉴于您的数据库具有适当的isolation level,即您只能读取未被更改的内容,数据库会处理此事。

你的"占据"可以使用值

将字段称为email_status
0 - new
1 - processing
2 - created
3 - confirmed 

最后一点是,这种簿记只是暂时的状态。你不想把所有那些多余的东西保留下来"确认"您的数据库中的条目,只是为了获得有关可能的错误的信息来自" new"到"确认"。

所以你甚至可以使用一个不同的表,由id和status组成,其中最后一步处理是删除它们。这样,您的附加表将只包含未完全处理的ID。

相关问题