An issue I’ve run into in the past is “how do we perform 5 calls to a 3rd party API, as quickly as possible?” The obvious answer is, make them all at once and wait until they’ve all come back! While PHP does support forking on Unix OSes http://www.tuxradar.com/practicalphp/16/1/3, the method I am going to explain allows for the requests to be load balanced, and the functionality also makes providing an API to our tools trivial! The client part of our code will run the following:
function getFood()
{
if(class_exists('\Memcache')) {
$cache = \Utilities\Utilities::getCache();
}
foreach (array('eggs','ham','span','jam') as $term) {
set_time_limit (60);
/**
* Here we are making GET requests for the API URLs, they will return a cacheId,
* which is like a cloak room ticket, that tells us where the results are when
* we need to come back and collect them later.
*/
if(isset($cache) && $cache != FALSE) {
$cacheIds[$number] = file_get_contents("http://example.com/webservice/searchterm/{$term}");
}
$number++;
}
if(isset($cacheIds)) {
$time = 0;
$responses = array();
$failcount = 0;
/**
* Loop until we have either processed all $cacheIds, been going 5 seconds, or had 10 cache failures.
*/
while(count($cacheIds) != count($responses) && $time < 5 && $failcount < 10) {
if($cache != FALSE) {
/**
* loop through all $cacheIds, to check if any of them have been populated in the cache.
*/
foreach($cacheIds as $id => $cacheId) {
if($responseData = $cache->get($cacheId)) {
$responses[$id] = $responseData;
unset($responseData);
$cache->delete($cacheId);
}
}
}
else {
/**
* If the cache has failed to be set, we try again! It may only have been a temporary issue.
*/
$cache = \Utilities\Utilities::getCache();
$failcount++;
}
/**
* The time needs incrementing, and then we sleep, to avoid wasting
* effort check for a network query too often!
*/
$time = $time+0.2;
sleep(0.2);
}
/**
* Check if we had any failures, and report them, otherwise it will be easy
* to miss that there is an issue.
*/
if($failcount > 0) {
trigger_error("Failed $failcount times",E_USER_WARNING);
}
/**
* Check if we ran out of time, if it's happening a lot, it would need investigation!
*/
if($time == 5) {
echo "Requests timed out";
}
}
if(!empty($responses)) {
foreach ($responses as $response) {
/**
* for the first pass, we need to initially set $returnResponse
* (and not try to do any merging!)
*/
if(!isset($returnResponse)) {
$returnResponse = $response;
}
else {
/**
* as long as the $response is an array, we now merge it with the former
* responses, to give our merged response.
*/
if(is_array($response)) {
$returnResponse = array_merge($returnResponse, $response);
}
}
}
return $returnResponse;
}
}
Hopefully that all made sense, the next part is the web service, which will immediately return an id, which the calling script can use to find the required data on a Memcache server.
If you don’t fancy using memcache APC, or even a file based system will work fine.
Now, the server side does the following:
function serveFood()
{
/**
* The $id will be the cloak room ticket. We simply echo it,
* and then close the connection (using "fastcgi_finish_request()")
*/
$id = uniqid();
echo $id;
fastcgi_finish_request();
/**
* Now that we've closed the connection, we will connect to the cache,
* inform that model that it is handling a 'webservice' request,
* and then store the results in the cache, and then we're all done!
* the results are waiting in the cloakroom, to be picked up shortly :-)
*/
$cache = \Utilities\Utilities::getCache();
$this->Search->setWebservice();
$results = $this->Search->search($this->userParams);
if($cache != FALSE) {
$cache->set($id, $results, 60);
}
else {
/**
* Of course, if it has all gone wrong, we need to know! So we trigger an error.
*/
trigger_error("SearchController::webserviceAction error: failed to connect to cache", E_USER_ERROR);
}
}</code></pre>
So, there we have it! Obviously this code is not just copy-and-pasteable, I’ve made use of my own classes, but if you read it through and impliment it to meet your own requirements it should give quite a headstart!