【问题标题】:How to send an email in a separate process with codeigniter如何使用 codeigniter 在单独的进程中发送电子邮件
【发布时间】:2019-06-26 18:25:55
【问题描述】:

我在 Web 应用程序中有一个错误报告功能,当用户发布错误时,会向开发人员电子邮件发送一封电子邮件。当前的工作流程类似于user input -> db write -> send email -> redirect

为了防止电子邮件功能阻碍用户进程的其余部分,我想将其分开,以便电子邮件发送进程可以做他们的事情,但用户可以继续工作。所以就像

user input -> db write --> redirect
                       \-> send email

我首先想到的是,我发送了一个 ajax 请求来启动电子邮件进程,以便错误报告和电子邮件功能可以异步工作,但我很好奇是否有更好的方法来做到这一点。

【问题讨论】:

  • 在你做任何事情之前,问问自己是否需要这种额外的复杂性,或者你是否正试图过早地进行优化。也许简单的 Ajax 请求和一个指示器就足够了。

标签: php email codeigniter-3


【解决方案1】:

根据您想要获得的复杂程度,有不同的方法可以做到这一点。 PHP 确实具有过程控制功能,但特别是如果您的网站将托管在共享托管服务提供商上,则无法保证该功能在您的托管环境中的可用性。

在确保跨各种托管环境的兼容性方面,可能更简单的方法之一是启动一个新的“要发送的邮件”数据库表,并为您要发送的每封邮件记录一行.然后在您的服务器上设置一个 cron 任务,该任务运行一个脚本,该脚本从该表中提取所有行,发送电子邮件,然后从表中删除该行。使用 CodeIgniter,如果您调用 index.php 脚本,如

php -f index.php foo bar

它将调用与通过标准路由系统调用/foo/bar 路径相同的方法(例如,Foo 控制器的bar 方法)。然后,在该方法中,您可以使用 if (!is_cli()) { exit(); } 确保不会从 Web 请求中调用该方法,并使用 flock() 确保您没有两个进程同时发送电子邮件(以防一个挂起或需要很长时间或其他东西)。从“要发送的邮件”表中选择所有内容,逐行遍历它们,发送邮件,如果适用,进行一些日志记录,然后删除该行。 (More info on doing CLI scripts on CodeIgniter.)

然后设置一个 cron 任务以每 X 分钟运行一次此脚本,也许设置一些方法来不时检查表以确保它不会变得太大(这将表明邮件发送脚本失败不知何故),然后就可以了。

如上所述,有更聪明的方法来实现这样的东西,但这是一种相当广泛兼容且万无一失的方法。

【讨论】:

  • 这是我的第二个想法,我第一次拒绝它的唯一原因是它没有立即发送。但我知道我在我的托管服务提供商那里有这个机会,如果没有更简单的解决方案,这可能是赢家解决方案
【解决方案2】:

可以从处理 Web 服务器请求的 PHP 脚本中分叉出一个进程,但您应该注意以下几点:

  • 您的原始 PHP 进程和分叉的进程可能会共享很多数据对象。虽然一些变量可以被复制并且可以在子进程和父进程中独立操作,但一些变量(如数据库连接或文件句柄等)可能引用相同的计算资源。您应该小心避免重复使用资源变量。让孩子重新连接到您的数据库并重新打开文件等。
  • 如果一个进程分叉,它可能无法连接到数据库或邮件网关等。您可能应该让这个分叉的子进程循环尝试完成它的目标,然后在一定次数的尝试后最终死掉,如果它是做一些重要的事情。
  • 可能还有十几个其他关于多线程、竞争条件、资源使用等的警告。请记住,分叉进程意味着您正在消耗更多资源,这些资源可能正在被其他进程使用,您可能会得到奇怪的、难以解决的问题。

说了这么多,我有一些运气使用exec 命令的组合来分叉一个单独的进程,然后调用posix_setsid 函数,以便分叉的子进程可以完全从 apache 中分离出来,并且即使父进程完成或 apache 重新启动或其他任何情况,也继续运行。

使用此代码需要您自担风险。

从您的 CodeIgniter 脚本中,您可以使用 exec 派生一个子进程,如下所示:

// the PARENT process (a CodeIgniter script running in response to http request)
// put this code in a controller or something

// here's a command to run the PHP executable in the background on some file
// routing the output and error messages to another file
$cmd = "/usr/bin/php /path/to/child-script.php > /tmp/foo/out.txt 2>&1 & echo \$!";

// will contain an array of output lines from the exec command
$cmd_output = NULL;

// will contain the success/failure code returned by the OS.
// Will be zero for a valid command, non-zero otherwise
$cmd_result = NULL; 

// $return will contain the last line from the result of
// the command which should be the PID of the process we have spawned
$cmd_return = exec ( $cmd, $cmd_output, $cmd_result );

注意:您可以改用pcntl_fork 来分叉该过程。我没有这样做的原因是因为这意味着子进程将继承 codeigniter 父脚本中的每个变量,这可能会导致一些令人困惑的行为。例如,如果一个进程更改了数据库连接,则该更改可能会突然出现在另一个脚本中,等等。此处使用 exec() 更彻底地将两个脚本分开并使事情变得更简单。如果您想更改上面的 $cmd 以运行 CodeIgniter 控制器,请参阅CodeIgniter from the Command Line

在子进程中,您必须调用 posix_setsid() 以便子脚本将自己与父脚本分离。如果您不这样做,那么子进程可能会在父进程完成(或被杀死)或网络服务器重新启动或崩溃或其他情况时被杀死。

这是子进程中的代码,它要求在 PHP.ini 中通过 disable_functions 指令安装、启用和禁用 POSIX 扩展。此扩展在您的 Web 服务器的 PHP.ini 中经常被禁用(并且有充分的理由),但有时仍可用于 CLI 脚本。另一个使用 exec 分叉命令行进程的好理由。

// child-script.php
// CHILD process. Put your emailing code or other long-running junk in here
// because this is a brand-new, separate process, you might need to load configuration files, connect to the db, etc.

if (!extension_loaded("posix")){
    throw new Exception("This script requires posix extension");
}

// NOTE: the output and results of this file will be completely unavailable to the parent script and you might never know what happens if you don't write a log file or something. Consider opening a log file somewhere

// process id of this process, should match contents of $cmd_return above
$mypid = posix_getpid();

// session id -- not to be confused with php session id
// apparently this call will make this process a 'session leader'
$sid = posix_setsid();
if ($sid < 0) {
    throw new Exception("setsid failed, returned $sid");
} 

// this output should be routed by the exec command above to
// /tmp/foo/out.txt
echo "setsid success, sid=$sid\n";

// PUT YOUR EMAILING CODE HERE

【讨论】:

  • 嗯,谢谢你详细的回答,但在我看来,这对于我需要的东西来说是一个矫枉过正的解决方案,但我可能会在本地环境中尝试一下,看看它是如何工作的
猜你喜欢
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 2014-01-26
  • 2017-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多