Здравствуйте. Ситуация следующая. Имеется скрипт для многопоточной загрузки с использованием multiCurl. Так вот по моему мнению он работает как то очень медленно на localhost`е(Denver) при работе в 50 потоков 500 файлов размером 13 байт загружались 177 секунд. Встречал сообщения что multiCurl запускали работать и на 1000 потоков и всё работало. В производительность компьютера скрипт точно не упирается (Core 2 Duo E8500, ОЗУ 4 Гб) повышения потребления памяти не заметил, загрузка ЦП 5-10%. PHP: function multiCurl($data) //Многопоточная загрузка данных { $time1=time(); $curls = array(); // Массив дескрипторов. Библиотека создает много экземпляров своего // механизма, но работать они будут параллельно $result = array(); //массив с результатами запрошенных страниц которые наша функция вернет. $mh = curl_multi_init(); // Дескриптор мульти потока. Тоесть эта штука отвечает за то, чтобы много // запросов шли параллельно. $uagent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8"; $header[] = "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; $header[] = "Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3"; $header[] = "Accept_charset: windows-1251,utf-8;q=0.7,*;q=0.7"; $header[] = "Accept_encoding: gzip,deflate"; $header[] = "Keep_alive: 300"; $header[] = "Connection: keep-alive"; foreach ($data as $IdRequest => $DataRequest) //Создание потока для закачки { $curls[$IdRequest] = curl_init(); // Для каждого url создаем отдельный curl механизм чтоб посылал запрос) curl_setopt($curls[$IdRequest], CURLOPT_URL, $DataRequest['url']); curl_setopt($curls[$IdRequest], CURLOPT_RETURNTRANSFER, 1); curl_setopt($curls[$IdRequest], CURLOPT_HEADER, 0); curl_setopt($curls[$IdRequest], CURLOPT_HTTPHEADER , $header); curl_setopt($curls[$IdRequest], CURLOPT_RETURNTRANSFER, 1); // возвращает веб-страницу curl_setopt($curls[$IdRequest], CURLOPT_ENCODING, ""); // обрабатывает все кодировки curl_setopt($curls[$IdRequest], CURLOPT_USERAGENT, $uagent); // useragent curl_setopt($curls[$IdRequest], CURLOPT_CONNECTTIMEOUT, 25); // таймаут соединения curl_setopt($curls[$IdRequest], CURLOPT_TIMEOUT, 25); // таймаут ответа curl_setopt($curls[$IdRequest], CURLOPT_FOLLOWLOCATION, 0); // переходит по редиректам //curl_setopt($curls[$IdRequest], CURLOPT_MAXREDIRS, 1); // останавливаться после 1-ого редиректа // добавляем текущий механизм к числу работающих параллельно curl_multi_add_handle($mh, $curls[$IdRequest]); } // число работающих процессов. $running = null; $time2=time(); // curl_mult_exec запишет в переменную running количество еще не завершившихся // процессов. Пока они есть - продолжаем выполнять запросы. do { curl_multi_exec($mh, $running); usleep(50); } while($running > 0); // Собираем из всех созданных механизмов результаты, а сами механизмы удаляем foreach($curls as $id => $c) { $result[$id] = curl_multi_getcontent($c); curl_multi_remove_handle($mh, $c); } $time3=time()-$time2; // Освобождаем память от механизма мультипотоков curl_multi_close($mh); echo 'Функция multiCurl завершена. Время работы всей функции/получение загружаемых данных (do while)'.(time()-$time1).'/'.$time3.'. Колличество загрузок(потоков):'.$x.'<br>'; // возвращаем данные собранные из всех потоков. ksort($result); return $result; } 1) файл 1 - создание файлов с случайным содержимым и файла для контроля корректности загрузку PHP: /** * Функция для создания файла и записи в него. Варианты работы: Дозапись файла, Перезапись файла * * @param string $Content содержимое файла * @param string $PathFiles Путь к файлу * @param Boolean $log Выбор варинта работы с файлом: Дозапись файла, Перезапись файла */ function save_to_files($Content,$PathFiles,$log) { if ($log==True) { $handle = fopen($PathFiles, "a"); //Дозапись файла } else {$handle = fopen($PathFiles, "wb");} //Перезапись файла if (fwrite($handle, $Content) === FALSE) { echo "<br/>Не могу произвести запись в файл"; exit; } if (!$handle) echo 'Ошибка открытия файла'; fclose($handle); } $stream=50; $CyclesTesting=10; if(!is_dir(".\TestMultiCurl")) mkdir(".\TestMultiCurl", 0700); for ($i=0;$i<($stream*$CyclesTesting);$i++) { $ContentFiles[$i]=$i.'_'.mt_rand(); save_to_files($ContentFiles[$i],'.\TestMultiCurl\test'.$i.'.txt',False); } //Сохраниени сгенерированного содержимого всех файлов save_to_files(implode("\r\n", $ContentFiles),'.\TestMultiCurl\CheckFiles.txt',False); 2) файл 2 - загрузка файлов и проверка корректности загрузки PHP: //вставить сюда функцию multiCurl <?php $Time=time(); $stream=50; $CyclesTesting=10; $UrlPath='http://testsait1/TestMultiCurl'; //$UrlPath='http://www.marketing-trade.ru/TestMultiCurl'; //Создание задания для загрузки файла с данными для провеки загрузок $Request[0] =Array('url' => $UrlPath.'/CheckFiles.txt', 'post' => '', 'cookie' => '', 'refer' => '', 'proxy' => ''); $CheckFiles=multiCurl($Request,0); unset($Request); echo '<br/>Затрачено времени на загрузку контрольного файла: '.(time()-$Time).'<br/>'; //деление загруженного файла на строки и помещение их в массив $ArrayCheckFiles=explode("\r\n", $CheckFiles[0]); //Загрухка файлов $i=0; unset($CheckFiles); $CheckFiles=array(); $i1=0; while($i<=($stream*$CyclesTesting)) { $i1++; $Request[$i] =Array('url' => $UrlPath.'/test'.$i.'.txt', 'post' => '', 'cookie' => '', 'refer' => '', 'proxy' => ''); if ($i1==$stream) { //var_dump($Request); $CheckFiles0=multiCurl($Request,0); $CheckFiles=array_merge($CheckFiles, $CheckFiles0); unset($CheckFiles0); unset($Request); $i1=0; } $i++; } if (count($Request)>0) { $CheckFiles0=multiCurl($Request,0); $CheckFiles=array_merge($CheckFiles, $CheckFiles0); unset($CheckFiles0); unset($Request); } //Провекра корректности загруженных файлов $ErrorCheck=0; foreach($ArrayCheckFiles as $id => $Content) { if ($Content!=$CheckFiles[$id]) { $ErrorCheck++; echo '<br/>ERROR:<br/>'.$Content.'<br/>'.$CheckFiles[$id]; } } echo '<br/><br/>Всего файлов/Ошибок: '.count($ArrayCheckFiles).'/'.$ErrorCheck; echo '<br/><br/>Затрачено времени: '.(time()-$Time); ?> 3) добавит в файл 2 функцию multiCurl 4) указать адрес где лежать загружаемые файлы $UrlPath Осталась надежда только на гуру с форума, подскажите пожалуйста почему происходит такая хрень, что я сделал неправильно? Протестируйте этот скрипт кто нибудь у себя, хотелось бы сравнить результаты.
Приходилось работать с мультикурлом, тоже обратил внимание на то что скорость вырастает не значительно, так же заметил что при работе свыше 10 потоков, не стабильно парсится контент. мб потому что это не многопоточность а эмуляция её.
Tisar Не стоит забывать несколько вещей: Курл открывает новое соединение на каждый запрос, то есть тратится дополнительное время на открытие соединения. Не в последнюю очередь на ваш тест влияют настройки апача - а конкретно количество процессов/потоков обрабатывающих входящие соединения При отправке HTTP запроса - генерируется множество заголовков, то есть вы получаете не 13 байт, а много больше Я бы посоветовал вам запустить например ApacheBench с тем же количеством потоков и запросов и посмотреть на реакцию сервера. Если разница с вашим скриптом будет существенна - ковыряйтесь в нем, если нет - настраивайте сервер.
А что смотреть в этом отчёте? Запуск(1) файла с выполняющего загрузку в 50 потоков запуск(500) файла с содержимым "echo 'test';": Тест выполнялся на localhost`е Denver
Tisar в первом случае вы делаете двойную работу. С помощью ab.exe вы должны скачивать сами файлы, или же один файл но 500 раз. А получается что вы скачиваете скрипт, скачивающий файлы. По второму тесту - скорость 32 кбайта - какая то слишком уж печальная, попробуйте увеличить количество потоков у апача И дополнительно сделайте все же тест на одновременные соединения: ab -n 500 -c 50
Да действительно дело оказалось в количестве потоков Апача (переменная ThreadsPerChild ) Тест при настройках ThreadsPerChild=60 Тест при настройках ThreadsPerChild=25 (по умолчанию)