Попытка реализации многопоточности в PHP

    Задача была в след: Нужно было получить координаты для множества городов и регионов.
    Т.к. координат было куча возникло решение в многопоточности.

    Плюсы: одновременное выполнение нескольких запросов к серверу.
    Минусы: если запускать свой скрипт, то надо указывать url к скрипту, т.е. на локале не всегда будет работать.

    Итак, сам класс:

    <?php
    /**************************************************************************************
    class:    Thread using curl_multi 
    version:  1.0 alfa
    Author:    Sect0R
    email:    odruslan@gmail.com
    **************************************************************************************/

    class thread
    {
      
      var $Urls;
      var $ReturnHeaders;
      var $FollowLocation;
      var $CookiesFile;
      var $ReturnValues;
      var $Errors;
      var $LastIndex;
      var $Header;
      var $Proxyes;
      var $POST;
      var $UserAgent;
      var $ConnectionTimeout;
      
      function Thread($UrlList = array()){
        $this->Urls = $UrlList;
        $this->ReturnHeaders = 0;
        $this->FollowLocation = 1;
        $this->CookiesVar = 'CookiesFile';
        $this->ReturnValues = array();
        $this->LastIndex = 0;
        $this->ConnectionTimeout = 20;
        $this->POST = array();
        $this->Proxyes = array();
        
          // Addition headers
        $this->UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.12) Gecko/20050919 Firefox/1.0.7" ;
        $this->Header [] = "Accept: text/html;q=0.9, text/plain;q=0.8, image/png, */*;q=0.5" ;
        $this->Header [] = "Accept_charset: windows-1251, utf-8, utf-16;q=0.6, *;q=0.1";
        $this->Header [] = "Accept_encoding: identity";
        $this->Header [] = "Accept_language: en-us,en;q=0.5";
        $this->Header [] = "Connection: close";
        $this->Header [] = "Cache-Control: no-store, no-cache, must-revalidate";
        $this->Header [] = "Keep_alive: 300";
        $this->Header [] = "Expires: Thu, 01 Jan 1970 00:00:01 GMT";
      }
      
      function normalize_url($url) {
          $url = trim($url);
          if (!preg_match('/http\:\/\//i',$url))
            $url = 'http://'.$url;
          return $url;
      }
        
      function AddUrl($url,$proxy = false) { // Add URL to List
        $this->Urls[$this->LastIndex] = $this->normalize_url($url);
        $this->Proxyes[$this->LastIndex] = $proxy;
        $this->LastIndex ++;
      }
      
      function CheckAll() { // Check All params
        // check file
        if (!file_exists($this->CookiesFile)) {
          $f = fopen($this->CookiesFile,"w");
          var_dump($f);
             fwrite($f,'');
             fclose($f);
          @chmod($this->CookiesFile,0777);
        }
      }
      
      
      function Run() { //Run process and return array with errors and results
          // Set PHP Variables
          @set_time_limit(0);
          @ignore_user_abort(true);
          @ini_set('memory_limit','512M');
          
            $this->CheckAll();
          
        $conn = array();
        print_r($this->Urls);
        $mh = curl_multi_init();
          foreach ($this->Urls as $i=>$url) {
            $conn[$i] = curl_init($url);
            if (!empty($this->Proxyes[$i]))
              @curl_setopt  ( $conn[$i] , CURLOPT_PROXY , $this->Proxyes[$i]);
            if (!empty($this->POST[$i])) {
              curl_setopt  ( $conn[$i] , CURLOPT_POST , 1);
              curl_setopt  ( $conn[$i] , CURLOPT_POSTFIELDS , http_build_query($this->POST[$i]));
            }
              @curl_setopt ( $conn[$i] , CURLOPT_FOLLOWLOCATION, $this->FollowLocation);
              @curl_setopt ( $conn[$i] , CURLOPT_CONNECTTIMEOUT, $this->ConnectionTimeout);
              @curl_setopt ( $conn[$i] , CURLOPT_RETURNTRANSFER, 1);
              @curl_setopt ( $conn[$i] , CURLOPT_HEADER, $this->ReturnHeaders);
              curl_setopt ( $conn[$i] , CURLOPT_COOKIEJAR, $this->CookiesFile);
                curl_setopt ( $conn[$i] , CURLOPT_COOKIEFILE, $this->CookiesFile);
              @curl_setopt ( $conn[$i] , CURLOPT_USERAGENT , $this->UserAgent );
              @curl_setopt ( $conn[$i] , CURLOPT_HTTPHEADER , $this->Header );
            curl_multi_add_handle ($mh,$conn[$i]);
          }
        // start performing the request
          do {
           $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);

        while ($active and $mrc == CURLM_OK) {
           if (curl_multi_select($mh) != -1) {
            do {
             $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
           }
        }
           // if Main Error
        if ($mrc != CURLM_OK) {
           return "Curl multi read error $mrc";
        }
        
        foreach ($this->Urls as $i => $url) {
             if (($err = curl_error($conn[$i])) == '') //if no errors
             {
              $this->ReturnValues[$i] = curl_multi_getcontent($conn[$i]);
             } else// if error
               $this->Errors[$i] = $err; 
             }
           curl_multi_remove_handle($mh,$conn[$i]); //delete handle
           curl_close($conn[$i]); // close curl
        }
        
      curl_multi_close($mh); // close main curl
      unset($mh);
      
        return $this->ReturnValues;
      }
      
    }
    ?>


    * This source code was highlighted with Source Code Highlighter.


    Ну и конечно тестирование:

    <?php
    $key = 'ABQIAAAAvPNKWsMGkmBeEsDOoHgLdBSlbMpHBbUVNY3iJpEhFSJCDcfQ1xSmosIIEHIwaN2v4JrRtuvHp4G6LA';

    $name1 = 'Belarus,Minsk';
    $name2 = 'Belarus,Grodno';
    $name3 = 'USA,New York';

    $request1 = "http://maps.google.com/maps/geo?q=".urlencode(trim($name1))."&output=csv&key={$key}";
    $request2 = "http://maps.google.com/maps/geo?q=".urlencode(trim($name2))."&output=csv&key={$key}";
    $request3 = "http://maps.google.com/maps/geo?q=".urlencode(trim($name3))."&output=csv&key={$key}";

    include_once("thread.class.php");

    $thread = new thread;

      $thread->AddUrl($request1);
      $thread->AddUrl($request2);
      $thread->AddUrl($request3);
      $thread->ReturnHeaders = 0;
      $thread->CookiesFile = getcwd().'/cook';

    // Run timer
    $begin_time = time();
      $result = $thread->Run();

    echo '<pre>';
    echo "RUNNING: ".(time()-$begin_time)." sec<br>";
    echo "RESULT:<br>";
    print_r($result);
    echo "<br><br>ERRORS:<br>";
    print_r($thread->Errors);
    echo '</pre>';
    ?>


    * This source code was highlighted with Source Code Highlighter.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 23

    • НЛО прилетело и опубликовало эту надпись здесь
    • НЛО прилетело и опубликовало эту надпись здесь
        0
        Интерессно и как ты хочешь реализовать её в php при помощи апатча?
        0
        Многопоточности здесь нет абсолютно. Вы не можете получить результат какого-либо уже выполненного запроса до того как завершится самый медленный из всех.
          0
          Именно в этом случае и не надо получать результат до того как всё не выполнилось.
          Чистая многопоточность была бы если аяксом уже дёргать по 1 городу. Но так явно хуже чем реализация автора.
            0
            В PHP её нету, но для поставленной задачи оно подошло идеально.
              0
              вопрос-то не в том, решена задача или нет, вопрос в том, зачем называть «попыткой реализации многопоточности» использование обычного функционала curl)
                0
                Вот именно что попытка.
                Я сделал распределение запросов на парралельные, и в описании класса не указано что идёт настоящая многопоточность. Нужна настоящая? Пользуйтесь или pcntl или system('php -f fork.php >>log_check.log & 2>/dev/null',$ret);
                  0
                  В последний раз когда имел дело с многопоточностью, то делал через мультикурл (кстати, столкнулся с cURL KNOWNBUG #65 при закачивании файлов на FTP с использованием прокси (и SOCKS и HTTP)), но реализовал это так, что у меня одновременно работало N потоков, и результат работы обрабатывался сразу (вызывалась callback функция, если поток закончился), после чего поток перезапускался с другим заданием и таким образом была достигнута ПОЧТИ реальная многопоточность =)
                    0
                    Не сразу заметил дату топика :)
          • НЛО прилетело и опубликовало эту надпись здесь
            • НЛО прилетело и опубликовало эту надпись здесь
                0
                а обычно, в средне-хостерном php нет этого pcntl_fork
                • НЛО прилетело и опубликовало эту надпись здесь
                    0
                    Если бы все хостеры это делали было бы супер, но к сожалению так делают процентов 5 от силы.
                    0
                    В любом среднехостерном Perl есть fork!!=)
                      0
                      Да, Perl, но не PHP.
                    0
                    На то она и ПОПЫТКА.
                      –1
                      И что ты хотел этим сказать? Что они очень похожи?

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое