Using PHP mail in a loop forces user to wait until the page displays
I have a forum, where users can subscribe to a particular thread. When someone replies to the thread, the reply is added to the DB and an email alert is sent to all the subscribers before the success page is displayed.
FYI - I am not using PHP's mail function, as my hosting provider has a limit of 500 per hour. I am using Google App Engine to send out the emails via curl.
The problem - when the number of subscribers is more than a hundred or so, the user has to wait for way too long before the success page is display开发者_开发问答ed, because PHP has to loop through each subscriber first. Is there a way to circumvent this without resorting to inserting the emails in the DB and then processing them via a cron?
Hope I've made sense - thanks in advance for your advice!
You can write your mail script and force it to run in the background using a shell command.
shell_exec("/path/to/php /path/to/send_notifications.php 'var1' 'var2' >> /path/to/alert_log/paging.log &");
Using the shell_exec
function of PHP, you can tell PHP to run a script in the background. The parameters you see in the shell_exec
command are seperated by spaces.
/path/to/php
- The actual path to PHP on the server (you should be able to just use 'php' here)/path/to/send_notifications.php
- The path to your mail script'var1' 'var2'
-$_SERVER['argv']
variables you can send to your script will be put in single quotes and spaced after the path to your script.>> /path/to/alert_log/paging.log
- Path to a log file (optional), anything echo'ed in your mail script will be written to this file&
- The most important part of this. The&
tells your server to run this script in the background
Doing it this way, when you execute the mail script, the user submitting the form will not have to wait for the script to finish. The browser will load the page faster and the user can browse away from the confirmation page or even close the browser and the script will continue to run until its completed.
If you using php as fastcgi controlled by php-pfm then you can use the fastcgi_finish_request() function. It's a very powerful function. It will close connection to browser when you call it but will continue to execute the rest of the script.
Ever since I first discovered this function, I've been using it and loving it.
No, the PHP process will not exit before all the API calls to GAE haven been executed. You could however send status updates to the user while the script is executing:
// in loop
// sent one message
echo "Sent email to user $email_user \n";
ob_flush();
ob_flush()
will force Apache to send the current buffer to the client. Alternatively you could show the mail page in a separate <div>
and call the mail script via ajax.
Edit: I guess it should be possible send the messages in batches to GAE? What kind of system is sitting at the GAE end (Python/Java)?
I think the problem is more with your implementation. Sending mail, especially in large quantities, is best done with forking or delayed jobs. The idea behind forking is that you enqueue "jobs" and execute them at a later time. The client is not stuck waiting for processes to complete...
One of the more popular projects for PHP is gearman.
Some other advantages to this approach are:
- error caching and automatic retries
- faster serving of pages
- you can write custom scripts to garbage collect, etc...
You need to move the email sending into a separate script. One way of doing this would be to create a mail_queue
table. So inside your loop, you would simply write a row to the database.
Then in another script, you would loop over the set of emails from the database to send out. This script can be run by a cron task, and it can just update that row to 'sent'.
I don't think you should not cron or something but you should use the task queue to do the processing offline!
App Engine applications can perform background processing by inserting tasks (modeled as web hooks) into a queue. App Engine will detect the presence of new, ready-to-execute tasks and automatically dispatch them for execution, subject to scheduling criteria.
In the webhook(of the taskqueue) you could make an asynchronous fetch to your webservice and this way to call will be made asynchronous which ensures your user does not have to wait for it to complete. I hope you understand my solution.
精彩评论