2014-04-27 3 views
3

libcurl을 통해 데이터를 검색하고 처리 한 PHP 스크립트를 작성했습니다. 괜찮 았지만 성능상의 이유로 수십 명의 직원 (스레드)을 사용하도록 변경했습니다. 성능이 50 배 이상 향상되었지만 이제는 php.exe가 몇 분마다 충돌하고 나열된 오류 모듈은 php_curl.dll입니다. 전 C에서 다중 스레딩에 대한 사전 경험이 있지만 PHP에서는 사용하지 않았습니다.PHP cURL - thread safe?

나는 주위를 봤 및 가정 컬 (2001 년 기준) 안전 스레드 : http://curl.haxx.se/mail/lib-2001-01/0001.html 하지만 여부 php_curl의 어떤 언급을 찾을 수 없습니다 스레드로부터 안전합니다.

중요한 경우, 나는 커맨드 라인에서 PHP를 실행하고있다. 내 설치는 Win7 x64, PHP 5.5.11 스레드 안전 VC11 x86, PHP pthreads 2.0.4 for PHP 5.5 스레드 안전 VC11 x86. 여기

내가

class MyWorker extends Worker 
{ 
    ... 
    public function run() 
    { 
     ... 
     while(1) 
     { 
      ... 
      runCURL(); 
      ... 
      sleep(1); 
     } 
    } 
} 

function runCURL() 
{ 
    static $curlHandle = null; 
    ... 
    if(is_null($curlHandle)) 
    { 
     $curlHandle = curl_init(); 
     curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, TRUE); 
     curl_setopt($curlHandle, CURLOPT_USERAGENT, "My User Agent String"); 
    } 
    curl_setopt($curlHandle, CURLOPT_URL, "The URL"); 
    curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $data); 
    curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $header); 
    curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, false); 

    $result = curl_exec($curlHandle); 
    ... 
} 
+0

필요는 –

+0

내가 생각 ... 당신이 정말로 가능성이 가장 높은 정적 아무것도하지 않으려는, 당신이 정말로 곱슬 곱슬하는 정적 핸들이 먼저 일 것입니다 싶지 않아 ... 실제 코드를 볼 수 있습니다 당신 말이 맞아, 나는 데이터를 가져 오는 사이트에서 일부 API 코드를 붙여 넣기 만 복사했다. 내가 이것을 게시 한 후에, 나는 깨달았고, 각 작업자 객체에 대한 핸들을 추가했다. – Matt

+0

핸들을 객체 범위에 저장하지 말고, 여러 메소드가 전달해야 할 경우 핸들을 로컬 범위에서 컬링 상태로 유지하십시오. 또는 일반 PHP OO를 사용하여 추상 cURL을 작성하고 스레드 된 객체의 객체 범위에 해당 객체를 쓰지 말아야합니다.이 객체는 작업자가 아니어야합니다. –

답변

7

첫째로하고있는 중이 야, resource 유형의 pthreads에 의해 공식적으로 지원되지 않는 것을 보여 일부 의사 코드; 컬 핸들은 resource이므로, 손상되었을 수 있으므로 pthreads 오브젝트 범위에 컬 핸들을 저장해서는 안됩니다.

을 : 그것은 쉽게

의 pthreads를 만들기

많은 스레드 사이에 실행하는 가장 쉬운 방법은이의 pthreads에서 제공 Pool 클래스에 내장 된 사용하는 것입니다 ... 근로자를 사용하는 쉬운 방법을 제공

http://php.net/pool

다음 코드는 몇 가지 백그라운드 스레드에 요청 묶음을 풀하는 방법을 보여줍니다.

<?php 
define("LOG", Mutex::create()); 

function slog($message, $args = []) { 
    $args = func_get_args(); 
    if (($message = array_shift($args))) { 
     Mutex::lock(LOG); 
     echo vsprintf("{$message}\n", $args); 
     Mutex::unlock(LOG); 
    } 
} 

class Request extends Threaded { 

    public function __construct($url, $post = []) { 
     $this->url = $url; 
     $this->post = $post; 
    } 

    public function run() { 
     $curl = curl_init(); 

     curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
     curl_setopt($curl, CURLOPT_URL, $this->url); 

     if ($this->post) { 
      curl_setopt($curl, CURLOPT_POSTFIELDS, $this->post); 
     } 

     $response = curl_exec($curl); 

     slog("%s returned %d bytes", $this->url, strlen($response)); 
    } 

    public function getURL()  { return $this->url;  } 
    public function getPost()  { return $this->post;  } 

    protected $url; 
    protected $post; 
} 

$max = 100; 
$urls = []; 
while (count($urls) < $max) { 
    $urls[] = sprintf(
     "http://www.google.co.uk/?q=%s", 
     md5(mt_rand()*count($urls))); 
} 

$pool = new Pool(4); 

foreach ($urls as $url) { 
    $pool->submit(new Request($url)); 
} 

$pool->shutdown(); 

Mutex::destroy(LOG); 
?> 

특정 작업이 이제 데이터를 처리해야합니다, 당신은 위의 같은 디자인에이 기능을 쓰기 또는

그것은 공상

약속이 최고입니다 제작 할 수 있습니다

    : 동시성의 화려한 형태 ...

    약속은 여기 작업의 성격에 맞게

  • 첫째, 프로세스 응답 다음 코드는 동일한 요청 및 프로세스 응답 할 pthreads/promises을 사용하는 방법을 보여줍니다

:

<?php 
namespace { 
    require_once("vendor/autoload.php"); 

    use pthreads\PromiseManager; 
    use pthreads\Promise; 
    use pthreads\Promisable; 
    use pthreads\Thenable; 

    define("LOG", Mutex::create()); 

    function slog($message, $args = []) { 
     $args = func_get_args(); 
     if (($message = array_shift($args))) { 
      Mutex::lock(LOG); 
      echo vsprintf("{$message}\n", $args); 
      Mutex::unlock(LOG); 
     } 
    } 

    /* will be used by everything to report errors when they occur */ 
    trait ErrorManager { 
     public function onError(Promisable $promised) { 
      slog("Oh noes: %s\n", (string) $promised->getError()); 
     } 
    } 

    class Request extends Promisable { 
     use ErrorManager; 

     public function __construct($url, $post = []) { 
      $this->url = $url; 
      $this->post = $post; 
      $this->done = false; 
     } 

     public function onFulfill() { 
      $curl = curl_init(); 

      curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
      curl_setopt($curl, CURLOPT_URL, $this->url); 

      if ($this->post) { 
       curl_setopt($curl, CURLOPT_POSTFIELDS, $this->post); 
      } 

      $this->response = curl_exec($curl); 
     } 

     public function getURL()  { return $this->url;  } 
     public function getPost()  { return $this->post;  } 
     public function getResponse() { return $this->response; } 
     public function setGarbage() { $this->garbage = true; } 
     public function isGarbage() { return $this->garbage; } 

     protected $url; 
     protected $post; 
     protected $response; 
     protected $garbage; 
    } 

    class Process extends Thenable { 
     use ErrorManager; 

     public function onFulfilled(Promisable $request) { 
      slog("%s returned %d bytes\n", 
       $request->getURL(), strlen($request->getResponse())); 
     } 
    } 

    /* some dummy urls */ 
    $max = 100; 
    $urls = []; 
    while (count($urls) < $max) { 
     $urls[] = sprintf(
      "http://www.google.co.uk/?q=%s", 
      md5(mt_rand()*count($urls))); 
    } 

    /* initialize manager for promises */ 
    $manager = new PromiseManager(4); 

    /* create promises to make and process requests */ 
    while (@++$id < $max) { 
     $promise = new Promise($manager, new Request($urls[$id], [])); 
     $promise->then(
      new Process($promise)); 
    } 

    /* force the manager to shutdown (fulfilling all promises first) */ 
    $manager->shutdown(); 

    /* destroy mutex */ 
    Mutex::destroy(LOG); 
} 
?> 

작곡가 :

{ 
    "require": { 
     "krakjoe/promises": ">=1.0.2" 
    } 
} 
  • 그런 요청을

    Request은 거의 변경되지 않았으며, 추가 된 항목은 모두 보관되는 것입니다. 응답 및 개체가 가비지 여부를 감지하는 수단.두 예에 적용 수영장에서 가비지 컬렉션에 대한 자세한 내용은

    :

    http://php.net/pool.collect

    slog 기능은 로그인 한 출력을 읽을 수

    이 제작하게 만들기 위해 존재 분명히

    pthreads는 새로운 PDO 드라이버가 아닙니다 ...

    많은 사람들이 새로운 PDO 드라이버를 사용하여 접근하기 때문에 pthreads을 사용하여 접근합니다. PHP의 나머지 부분처럼 작동하고 모든 것이 잘 될 것이라고 가정합니다.

    모든 것이 잘 될 및 연구를 필요로하지 않을 수 있습니다 :을 우리가 안정성을 유지하기의 pthreads의 구조를 배치해야합니다 일부 "제한"과정에서 봉투를 추진하고있다, 이것은 몇 가지 이상한 부작용이있을 수 있습니다.

    pthreads는 대부분 PHP 매뉴얼에 예제가 들어있는 철저한 문서와 함께 제공되지만 매뉴얼에는 다음 문서를 아직 첨부 할 수 없습니다.

    다음 문서는 pthreads의 내부를 이해하고 모든 사람이 읽어야하며의 경우 으로 작성되었습니다.

    https://gist.github.com/krakjoe/6437782

  • +1

    확실히 좋은 대답입니다. 나는이 정도, 또는 대답을 그렇게 빨리 예상하지 못했습니다. 고마워, 조. – Matt

    +0

    항상 즐거움;) –