Articles from July 2010
Posted on Wednesday, July 7, 2010 at 5:11AM by Jack Servedio
When iterating through a long queue and executing a web service on each item, especially time consuming web services, where getting a response from the web service is critical, a simple for or while loop can take an extremely long time to run. This is because PHP can only execute one web service at a time with the loop, and it has to wait for the response to the web service before it can continue on to the next item. In some cases, you can simply have PHP not wait for a response and continue on to the next item to speed things up, but what about when the web service's response is critical to your application?
I ran into this problem while working on a system to send out SMS Messages in a queue implemented with MySQL through the Ericsson Gateway. To send a message through the Ericsson Gateway, you have to make a SOAP request to their web service and pass along a number of parameters, such as username, password, short code, phone number, message body, etc. This system has to catch any errors in sending the request to Ericsson's web service to ensure that the message would make it into the aggregator queue. If the web service returns an error, the SMS message must be re-sent through the web service. As you can see, the only way to do this is to wait for the response from the web service and handle it.
When running through a queue with a simple loop, using the NuSoap class to make the SOAP request to Ericsson, the script would run at between .75 and 1.5 messages per second. This may seem decent, but when you are sending out blasts of over 100,000 messages, and your maximum inbound message rate at the aggregator is 20 messages per second, this is far too slow.
If you have to wait for the web service request to be returned, and it is taking between 0.5 and 1.5 seconds to be returned, and PHP isn't using resources while waiting for the web service to be returned, the logical way to speed things up is to run multiple web services all at once. But how do you do this while maintaining queue integrity, keeping the web services from running away and executing thousands at once, and most importantly, how to handle and retry web service errors? The best way to do this is by executing each web service in its own process while still sharing a single queue. To execute multiple requests at once, each with its own individual process, while maintaining queue integrity and handling errors, the best solution is Forking using the PCNTL and POSIX Process Control Extensions. Below is my proof of concept using Forking to iterate through a MySQL queue, keeping its integrity using transactions, and allowing a limited number processes at once to be run.
Download the Source Here and take a look at the code. I have included the MySQL database table structure along with a rudimentary database class to handle queries and returned data, the NuSoap Library, and the script (forking_api.php). The configuration allows you to set a maximum number of forks (instances of the script running in parallel, each instance with its own PID), the amount of time to wait before attempting to fork if the maximum number of forks is reached, and a maximum number of times to try waiting before shutting down. The script starts by instantiating a NuSoap client object to make the SOAP request and connecting to the database to access the queue. Next, the script begins a transaction and queries the queue for the next unsent message. When the script queries the queue for the next message, it locks that row, which keeps integrity of the queue when multiple instances of the script are accessing the queue all at once. The query gets the message ID for indexing and the phone number, short code, and message body from the queue to make the SOAP request to the aggregator. If the queue is empty, it will stop the script. If the query retrieved a message from the queue, it will set the message as sent and commit the transaction. Now is where it gets interesting. The script will now fork. When a script forks, it runs everything below the fork in two processes, meaning everything below the fork is run twice. However, using POSIX, you can make your script aware of its process ID, and do different actions based on its process ID. If the script detects that it is running in a new process, it will check to see if the maximum number of forks has been reached, and if it has, go into a holding pattern. If the maximum number of forks has not been reached, it will use passthru to open up a new instance of itself in a new process ID and then close. Now you are left with the original script, in the original process ID, at the point in the script where it forked, and another instance of forking_api.php at line 1. The original process will execute the SOAP request and wait for the response. If the web service executes with no errors, the script will end. However, if there is an error, it will revert the message to unsent, putting it at the front of the queue. While the SOAP request is executing, your second instance of the script will keep running, keep forking, and keep executing SOAP requests until it reaches the maximum number of forks.
By using forking, you can ensure that you have a pre-defined number of instances of your SOAP request running asynchronously at all times until the queue is empty. By using this method, you are only limited by the amount of processing power, memory, and bandwidth your servers have. Using this script on a message queue, I was able to sustain 10 - 20 messages per second or more for extended periods of time.
Please remember this script is only a proof of concept, and is not intended to go directly into production so use it at your own risk.
Download Source - Contains self forking script, NuSoap Library, a very rudimentary database class, and a SQL file containing the table structure for the queue used.
Posted on Wednesday, July 7, 2010 at 12:44AM by Jack Servedio
Sending an e-mail, whether plain-text or HTML, through an SMTP server, with authentication, in PHP can be done fairly simply using the PEAR Extension. Although sending an SMTP e-mail is fairly simple in concept, it requires a lot of steps to implement which subsequently creates a lot of lines of code. Looking at an example from a PEAR forum, you can see sending a simple HTML e-mail requires you to create an SMTP object, generate headers, and create a Mime object. After creating the Mime object, you have to set the body HTML, Plaintext, From, and Subject from configuration using individual member functions and then use the get method to create the body of the e-mail. Once you have a body, you have to create the e-mail's headers manually from an array. After the e-mail is all ready to send, you have to create a Mail object which will allow you to authenticate with a SMTP server specified by hostname, username, password, and an authentication flag. After you are authenticated and all the other parts of the e-mail are set up, you can call the send function to put everything together and send the e-mail. To send a simple e-mail, you may end up writing 20 - 60 lines of code, not including your configuration variables storing things like hostname, username, password, etc. Sending additional e-mails isn't so simple either, and requires a new Mime object.
However, the class I have written will do all of the heavy lifting by creating objects and putting everything together automatically. You can send HTML e-mails with 2 lines of code, excluding configuration, and send additional e-mails with a single line of code. You can also access the objects created by the class directly if needed or through neat accessor methods. Instead of writing all of the code required in the example from the PEAR forum, your code can look like this:
//Include Class and SMTP Server Configuration
//Instantiate new Class using variables from Config
$email = new SMTPMail( $smtp_host, $smtp_user, $smtp_pass, $smtp_from );
//Set To E-Mail Address, Subject, and (HTML) E-Mail Body [Optionally Plaintext Replacement if HTML Disaabled]
$to = "email@example.com";
$subject = "SMTPMail Class Test E-Mail";
$body = "<h1>SMTPMail Class Test Message</h1><p>This message was sent using the SMTPMail Class which easily sends HTML Based E-Mails using the PEAR Mail Package</p>";
//Send the E-Mail Message
$email->sendMail( $to, $subject, $body );
//Output the E-Mail Status Message
As you can see, this is far simpler than writing all of the code in the example, and wraps up all the functionality in one, easy to use class. You can re-use the class to keep sending additional e-mails from the SMTP server defined in the constructor by simply calling the sendMail method.
Download Source - Source includes the SMTPMail.class.php, a configuration file, and a sample driver to explain how to use the class.
- Page 1 of 1
- << First
- Last >>