ThomasRedstone

Distributed Concurrency in PHP using Nginx and Memcache

Nov 1st, 2013
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 5.23 KB | None | 0 0
  1. /*
  2.  
  3. Distributed Concurrency in PHP using Nginx, PHP5-FPM and Memcache
  4. 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!
  5. 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!
  6. The client part of our code will run the following:
  7.  
  8. */
  9.  
  10.         if(class_exists('\Memcache')) {
  11.             $cache = \Utilities\Utilities::getCache();
  12.         }
  13.         foreach (array('eggs','ham','span','jam') as $term) {
  14.             set_time_limit (60);
  15.            
  16.             /**
  17.             * Here we are making GET requests for the API URLs, they will return a cacheId,
  18.             * which is like a cloak room ticket, that tells us where the results are when
  19.             * we need to come back and collect them later.
  20.             */
  21.             if(isset($cache) && $cache != FALSE) {
  22.                 $cacheIds[$number] = file_get_contents("http://example.com/webservice/searchterm/{$term}");
  23.             }
  24.             $number++;
  25.         }
  26.         if(isset($cacheIds)) {
  27.             $time = 0;
  28.             $responses = array();
  29.             $failcount = 0;
  30.             /**
  31.             * Loop until we have either processed all $cacheIds, been going 5 seconds, or had 10 cache failures.
  32.             */
  33.             while(count($cacheIds) != count($responses) && $time < 5 && $failcount < 10) {
  34.                 if($cache != FALSE) {
  35.                     /**
  36.                     * loop through all $cacheIds, to check if any of them have been populated in the cache.
  37.                     */
  38.                     foreach($cacheIds as $id => $cacheId) {
  39.                         if($responseData = $cache->get($cacheId)) {
  40.                             $responses[$id] = $responseData;
  41.                             unset($responseData);
  42.                             $cache->delete($cacheId);
  43.                         }
  44.                     }
  45.                 }
  46.                 else {
  47.                     /**
  48.                     * If the cache has failed to be set, we try again! It may only have been a temporary issue.
  49.                     */
  50.                     $cache = \Utilities\Utilities::getCache();
  51.                     $failcount++;
  52.                 }
  53.                 /**
  54.                 * The time needs incrementing, and then we sleep, to avoid wasting
  55.                 * effort check for a network query too often!
  56.                 */
  57.                 $time = $time+0.2;
  58.                 sleep(0.2);
  59.             }
  60.             /**
  61.             * Check if we had any failures, and report them, otherwise it will be easy
  62.             * to miss that there is an issue.
  63.             */
  64.             if($failcount > 0) {
  65.                 trigger_error("Failed $failcount times",E_USER_WARNING);
  66.             }
  67.             /**
  68.             * Check if we ran out of time, if it's happening a lot, it would need investigation!
  69.             */
  70.             if($time == 5) {
  71.                 echo "Requests timed out";
  72.             }
  73.         }
  74.         if(!empty($responses) || !class_exists('\Memcache')) {
  75.             foreach ($responses as $response) {
  76.                 /**
  77.                 * for the first pass, we need to initially set $returnResponse
  78.                 * (and not try to do any merging!)
  79.                 */
  80.                 if(!isset($returnResponse)) {
  81.                     $returnResponse = $response;
  82.                 }
  83.                 else {
  84.                     /**
  85.                     * as long as the $response is an array, we now merge it with the former
  86.                     * responses, to give our merged response.
  87.                     */
  88.                     if(is_array($response)) {
  89.                         $returnResponse = array_merge($returnResponse, $response);
  90.                     }
  91.                 }
  92.             }
  93.             return $returnResponse;
  94.         }
  95.  
  96. /*
  97.  
  98. Now, the server side does the following:
  99.  
  100. */
  101.  
  102.         /**
  103.         * The $id will be the cloak room ticket. We simply echo it,
  104.         * and then close the connection (using "fastcgi_finish_request()")
  105.         */
  106.         $id = uniqid();
  107.        
  108.         echo $id;
  109.         fastcgi_finish_request();
  110.  
  111.         /**
  112.         * Now that we've closed the connection, we will connect to the cache,
  113.         * inform that model that it is handling a 'webservice' request,
  114.         * and then store the results in the cache, and then we're all done!
  115.         * the results are waiting in the cloakroom, to be picked up shortly :-)
  116.         */
  117.  
  118.         $cache = \Utilities\Utilities::getCache();
  119.        
  120.         $this->Search->setWebservice();
  121.        
  122.         $results = $this->Search->search($this->userParams);
  123.        
  124.         if($cache != FALSE) {
  125.             $cache->set($id, $results, 60);
  126.         }
  127.         else {
  128.             /**
  129.             * Of course, if it has all gone wrong, we need to know! So we trigger a warning.
  130.             */
  131.             trigger_error("SearchController::webserviceAction error: failed to connect to cache", E_USER_ERROR);
  132.         }
Advertisement
Add Comment
Please, Sign In to add comment