2012-02-10 31 views
7

Tôi phải xóa một trang web nơi tôi cần tìm nạp nhiều URL và sau đó xử lý từng URL một. Quá trình hiện tại hơi giống như thế này.Tải xuống các trang song song bằng cách sử dụng PHP

Tôi tìm nạp URL cơ sở và nhận tất cả URL phụ từ trang này, sau đó cho mỗi url phụ tôi tìm nạp URL đó, trang tìm thấy quy trình, tải xuống một số ảnh (mất nhiều thời gian) và lưu trữ dữ liệu này vào cơ sở dữ liệu, sau đó tìm nạp URL tiếp theo và lặp lại quy trình.

Trong quá trình này, tôi nghĩ tôi đang lãng phí thời gian khi tìm nạp URL phụ ở đầu mỗi lần lặp. Vì vậy, tôi đang cố gắng tìm nạp các URL tiếp theo song song trong khi xử lý lần lặp đầu tiên.

Giải pháp trong đầu tôi là từ quá trình chính gọi tập lệnh PHP, trình tải xuống sẽ tải xuống tất cả URL (với curl_multi hoặc wget) và lưu trữ chúng trong một số cơ sở dữ liệu.

Câu hỏi của tôi là

  • Làm thế nào để gọi như vậy downloder không đồng bộ, tôi không muốn kịch bản chính của tôi đợi cho đến khi downloder hoàn tất.
  • Bất kỳ vị trí nào để lưu trữ dữ liệu đã tải xuống, chẳng hạn như bộ nhớ dùng chung. Tất nhiên, ngoài cơ sở dữ liệu.
  • Có bất kỳ cơ hội nào dữ liệu bị hỏng trong khi lưu trữ và truy xuất, cách tránh điều này?
  • Ngoài ra, vui lòng cho tôi biết nếu có ai có kế hoạch tốt hơn.
+2

PHP không thực sự được thiết kế để khởi chạy nhiều quy trình. Tại sao không nhìn vào một ngôn ngữ như python để thực hiện điều này? – afuzzyllama

+0

@afuzzyllama Nó chỉ là một mô-đun phụ, toàn bộ dự án là trong PHP –

+0

[nodejs] (http://nodejs.org) sẽ là hoàn hảo cho việc này. – Xeoncross

Trả lời

6

Khi tôi nghe ai đó sử dụng curl_multi_exec, thường thì họ chỉ tải nó với 100 url, sau đó đợi khi hoàn tất, sau đó xử lý tất cả, sau đó bắt đầu lại với 100 url tiếp theo ... Tôi đã làm như vậy, nhưng sau đó tôi phát hiện ra rằng có thể loại bỏ/thêm các chốt xử lý vào curl_multi trong khi vẫn còn một thứ gì đó, và nó thực sự tiết kiệm rất nhiều thời gian, đặc biệt nếu bạn sử dụng lại các kết nối đã mở. Tôi đã viết một thư viện nhỏ để xử lý hàng đợi các yêu cầu với các cuộc gọi lại; Tôi không đăng phiên bản đầy đủ ở đây tất nhiên ("nhỏ" vẫn là khá nhiều code), nhưng đây là một phiên bản đơn giản của điều chủ yếu để cung cấp cho bạn những ý tưởng chung:

public function launch() { 
    $channels = $freeChannels = array_fill(0, $this->maxConnections, NULL); 
    $activeJobs = array(); 
    $running = 0; 
    do { 
     // pick jobs for free channels: 
     while (!(empty($freeChannels) || empty($this->jobQueue))) { 
      // take free channel, (re)init curl handle and let 
      // queued object set options 
      $chId = key($freeChannels); 
      if (empty($channels[$chId])) { 
       $channels[$chId] = curl_init(); 
      } 
      $job = array_pop($this->jobQueue); 
      $job->init($channels[$chId]); 
      curl_multi_add_handle($this->master, $channels[$chId]); 
      $activeJobs[$chId] = $job; 
      unset($freeChannels[$chId]); 
     } 
     $pending = count($activeJobs); 

     // launch them: 
     if ($pending > 0) { 
      while(($mrc = curl_multi_exec($this->master, $running)) == CURLM_CALL_MULTI_PERFORM); 
       // poke it while it wants 
      curl_multi_select($this->master); 
       // wait for some activity, don't eat CPU 
      while ($running < $pending && ($info = curl_multi_info_read($this->master))) { 
       // some connection(s) finished, locate that job and run response handler: 
       $pending--; 
       $chId = array_search($info['handle'], $channels); 
       $content = curl_multi_getcontent($channels[$chId]); 
       curl_multi_remove_handle($this->master, $channels[$chId]); 
       $freeChannels[$chId] = NULL; 
        // free up this channel 
       if (!array_key_exists($chId, $activeJobs)) { 
        // impossible, but... 
        continue; 
       } 
       $activeJobs[$chId]->onComplete($content); 
       unset($activeJobs[$chId]); 
      } 
     } 
    } while (($running > 0 && $mrc == CURLM_OK) || !empty($this->jobQueue)); 
} 

Trong phiên bản của tôi $ công việc thực sự là lớp riêng biệt, không phải là trường hợp của bộ điều khiển hoặc mô hình. Họ chỉ xử lý các tùy chọn thiết lập cURL, phân tích cú pháp phản hồi và gọi một cuộc gọi lại đã cho onComplete. Với cấu trúc này, các yêu cầu mới sẽ bắt đầu ngay sau khi một cái gì đó ra khỏi hồ bơi kết thúc.

Tất nhiên nó không thực sự giúp bạn tiết kiệm nếu không chỉ lấy mất thời gian nhưng chế biến là tốt ... Và nó không phải là một xử lý song song đúng sự thật. Nhưng tôi vẫn hy vọng nó sẽ giúp. :)

P.S. đã làm một thủ thuật cho tôi. :) Khi công việc 8 giờ hoàn thành trong 3-4 phút bằng cách sử dụng một nhóm gồm 50 kết nối. Không thể diễn tả cảm giác đó. :) Tôi đã không thực sự mong đợi nó hoạt động như kế hoạch, bởi vì với PHP nó hiếm khi hoạt động chính xác như ... Nó giống như "ok, hy vọng nó kết thúc trong ít nhất một giờ ... Wha ... Đợi .. 8-O "

+0

Cảm ơn bạn đã chia sẻ, bạn cũng có thể tìm thấy các thư viện khác như thế này nếu bạn tìm kiếm github. – Xeoncross

+0

Câu trả lời thực sự hữu ích, nhưng nhận xét của bạn đã giúp tôi rất nhiều, cảm ơn người đàn ông ... –

+0

Chào mừng bạn. :) Tôi đoán tôi phải đặt câu trả lời đó vào câu trả lời rồi ... – Slava

2

Bạn có thể sử dụng curl_multi: http://www.somacon.com/p537.php

Bạn cũng có thể muốn xem xét việc làm phía khách hàng này và sử dụng Javascript.


Một giải pháp khác là viết một thợ săn/hái lượm mà bạn gửi một loạt các URL, sau đó chiến dịch làm việc song song và trả về một mảng JSON sau khi nó được hoàn thành.

Đặt một cách khác: nếu bạn có 100 URL, bạn có thể POST mảng đó (có thể là JSON) tới mysite.tld/huntergatherer - nó thực hiện bất kỳ thứ gì bạn muốn bằng bất kỳ ngôn ngữ nào bạn muốn và chỉ trả về JSON.

+0

Yeh, tôi đã sử dụng curl_multi, javascript có vẻ tốt, tôi có thể lấy thêm chi tiết không? –

0

Hãy thử thực thi từ PHP, tập lệnh python-pycurl. Dễ dàng hơn, nhanh hơn so với PHP curl.

2

Ngoài giải pháp đa curl, một số khác chỉ có một lô là gearman workers. Nếu bạn đi tuyến đường này, tôi đã tìm thấy supervisord một cách hay để bắt đầu tải công nhân deamon.

+0

Cảm ơn! Đã không bao giờ nghe nói về phần mở rộng này trước đây. – Slava

1

Những điều bạn nên xem xét thêm vào curl đa:

  • suối Non-blocking (ví dụ: PHP-MIO)
  • ZeroMQ đẻ ra nhiều công nhân có làm yêu cầu không đồng bộ

Trong khi node.js, ruby ​​EventMachine hoặc các công cụ tương tự là khá tuyệt vời để làm công cụ này, những thứ tôi đã đề cập làm cho nó khá dễ dàng trong PHP quá.

+0

Không chặn luồng thực sự là một đọc tốt, xin lỗi nhưng tôi có thể chọn chỉ có một câu trả lời là tốt nhất. cảm ơn –

Các vấn đề liên quan