Как отправить электронное письмо в отдельном процессе с помощью codeigniter - PullRequest
1 голос
/ 26 июня 2019

У меня есть функция сообщения об ошибках в веб-приложении, и когда пользователь публикует сообщение об ошибке, электронное письмо отправляется на электронную почту разработчика.Текущий рабочий процесс похож на user input -> db write -> send email -> redirect.

Чтобы не допустить, чтобы функция электронной почты сдерживала остальную часть пользовательского процесса, я хочу отделить ее, чтобы процесс отправки электронной почты мог выполнять свои функции, но пользователи могли продолжать работать.Это похоже на

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

Первое, что мне пришло в голову, это то, что я отправляю ajax-запрос для запуска процесса электронной почты, чтобы отчеты об ошибках и функция электронной почты могли работать асинхронно, но мне любопытно, если естьбыло бы лучшим способом сделать это.

Ответы [ 2 ]

1 голос
/ 26 июня 2019

Возможно вычеркнуть процесс из PHP-скрипта, который обрабатывает запрос веб-сервера, но вам следует остерегаться нескольких вещей:

  • Ваш оригинальный процесс PHP и разветвленный процесс будутвероятно, разделяют много объектов данных.Хотя некоторые переменные могут быть скопированы и могут обрабатываться независимо в дочернем и родительском процессах, некоторые переменные (например, соединения db или файловые дескрипторы и т. Д.) Могут ссылаться на один и тот же вычислительный ресурс.Вы должны быть осторожны, чтобы избежать повторного использования переменных ресурса.Попросите ребенка повторно подключиться к вашей БД и заново открыть файлы и т. Д.
  • Если процесс отключается, у него могут возникнуть проблемы с подключением к БД или почтовому шлюзу и т. Д. Вероятно, вы должны сделать этот разветвленный дочерний цикл процесса следующимпопытайтесь достичь своей цели и затем в конечном итоге умрете после определенного числа попыток, если он делает что-то важное.
  • Вероятно, есть еще дюжина других предупреждений о многопоточности, условиях гонки, использовании ресурсов и т. д. Просто помните, что отключениепроцессы означают, что вы потребляете больше ресурсов, что эти ресурсы могут использоваться другими процессами, и у вас могут возникнуть странные, трудно устраняемые проблемы.

Все это говорит, у меня были некоторыеудачи, используя комбинацию команды exec для отключения отдельного процесса, а затем вызывая функцию posix_setsid , чтобы разветвленный дочерний процесс мог полностью отсоединиться от apache и продолжить работу дажеесли родительский процесс завершается или apache перезапускается или whatever.

ИСПОЛЬЗУЙТЕ ЭТОТ КОД НА СВОЙ СТРАХ И РИСК.

С помощью скрипта 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 из командной строки .

В дочернем процессе вы должны вызвать posix_setsid (), чтобы дочерний скрипт отключалсясам от родителя.Если вы этого не сделаете, то дочерний процесс может быть уничтожен, когда родительский процесс завершится (или будет уничтожен), или если веб-сервер перезапустится или выйдет из строя или что-то в этом роде.

Вот код в дочернем процессе, который требуетрасширение POSIX, которое должно быть установлено, включено и не отключено в PHP.ini с помощью директивы disable_functions .Это расширение очень часто отключается (и по уважительной причине) в 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
1 голос
/ 26 июня 2019

Есть разные способы сделать это в зависимости от того, насколько сложным вы хотите стать.В PHP есть средства управления процессами, но особенно если ваш сайт будет размещаться у провайдера общего хостинга, доступность этой функции в вашей среде хостинга не гарантируется.

Вероятно, это один из наиболее надежных способовсделать это с точки зрения обеспечения совместимости с широким спектром хостинговых сред - это запустить новую таблицу базы данных "mails to send" и зарегистрировать в ней строку для каждой почты, которую вы хотите отправить.Затем настройте задачу cron на вашем сервере, которая запускает скрипт, который извлекает все строки из этой таблицы, отправляет электронное письмо, а затем удаляет строку из таблицы.С CodeIgniter, если вы вызываете скрипт index.php, например

php -f index.php foo bar

, он вызовет тот же метод, который будет вызван, если путь /foo/bar вызывается через стандартную систему маршрутизации.(например, метод bar контроллера Foo).Затем внутри этого метода вы можете использовать if (!is_cli()) { exit(); }, чтобы убедиться, что метод не вызывается из веб-запроса, и flock(), чтобы убедиться, что у вас нет двух процессов, отправляющих электронные письма наодин раз (в случае, если один завис или занимает много времени или что-то).Выберите все из таблицы «письма для отправки», перебирайте их строка за строкой, отправляйте почту, ведите журнал, если это применимо, затем удаляйте строку.( Дополнительная информация о выполнении сценариев CLI в CodeIgniter. )

Затем настройте задачу cron для запуска этого сценария каждые X минут, возможно, настройте какой-либо способ проверки таблицы время от времени, чтобыубедитесь, что он не становится слишком большим (что будет признаком того, что скрипт отправки почты как-то не работает), и все готово.

Как уже упоминалось выше, есть более умные способы реализовать что-то подобное, ноэто довольно широко совместимый и надежный.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...