PHP относится к языкам, в которых поддержка многопоточности отсутствует. Но, есть немало задач, в которых она была бы очень полезна. В большинстве случаев вам не нужно порождать и создавать новых потоков вообще и можно получить отличную производительность и без этого.
СокетыОчень полезная штука, когда вам надо дёргать чужие страницы или просто посылать запросы к другим сайтам.
Недавно поступил заказ на скрипт, который будет дёргать информацию с одного сайта, сохранять в базу, а потом переносить на другой. Порой скрипт делает запросы к 100+ страницам. Если этот скрипт бы выполнялся последовательно, то заняло бы много времени.
В этом деле поможет функция stream_socket_client
Прелесть в том, что, создавая запросы к сайтам этой функцией в асинхронном режиме, нам не придётся ждать ответа. Задача состоит из двух частей.
Создать сокеты$flag = STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT;$sockets=array();$id=0;$timeout = 60;$convenient_read_block=8192;$result=array(); $url = array();for($i=$this->beg_ID;$i<=$this->end_ID;++$i){$fp = stream_socket_client('tcp://'.$host.':'.$this->port, $err, $errstr, 8, $flag);if (!$fp) {echo 'httpPost error: '.$errstr;return NULL;}else{stream_set_blocking($fp, 0); $sockets[$id++] = $fp;$url[$id-1] = $i;}}
Как видно, создаются сокеты и ссылки на них сохраняются в массиве $sockets
. Теперь необходимо организовать прослушку ответов.
В некоторых источниках видел исходники, где функция на запись в сокет вызывается сразу после создания(применяли к $fp
). На самом деле необходимо организовать прослушку на готовность сокета и к чтению, и к записи.
//создаём массивы сокетов для прослушки$toRead = $toWrite = $sockets; //пока есть сокеты, чтение с которых мы ждёмwhile (count($toRead) > 0) {$read=$toRead;$write=$toWrite; stream_select($read, $write, $e=null, $timeout); //есть сокеты, готовые к записиif (count($write)){foreach ($write as $w){$id=array_search($w, $sockets); //$query="GET /product.php?pid=PD".$id." HTTP/1.0\r\n".$query="GET /product.php?pid=PD0".$url[$id]." HTTP/1.0\r\n"."Host: ".$host."\r\n"."User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1\r\n"."Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8"."Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3\r\n"."Accept-Encoding: gzip, deflate\r\n"."Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\r\n"."Connection: close\r\n\r\n";fwrite($w, $query);unset($toWrite[array_search($w, $toWrite)]);}}//есть сокеты, готовые к чтениюif (count($read)) {foreach ($read as $r) { $id=array_search($r, $sockets); if(!isset($result[$id]))$result[$id]='';$data=fread($r, $convenient_read_block); if (strlen($data) == 0) { fclose($r); unset($toRead[array_search($r, $toRead)]);} else { $result[$id] .= $data; } } }}
Мы используем функцию stream_select()
для ожидания возникновения событий на открытых сокетах. Вы можете ожидать готовности чтения, записи или исключительных событий (параметр первый, второй и третий соответственно). stream_select()
будет ждать $timeout
секунд пока событие не появится – когда же это случится, функция будет модифицировать массивы, которые Вы ей передали, так что они будут содержать идентификаторы сокетов, удовлетворяющих Вашему критерию.
Ну, как примечание, скажу, что доступна эта прелесть с PHP 5.
Хм… Как раз на эту тему думал, а тут такой пост шикарный, спасибо!
Разместил это на своем блоге с ссылкой на ваш сайт. Надеюсь, Вам это какую-нибудь пользу принесет
В твиттере, например)
Подписался на RSS, буду следить =)
))) видно, что автор постарался, полодец ;) продолжай в том же духе!
Круто. Добавлю блог в избранное и друзьям посоветую. Ждите новых читателей :)
На сокетах замучаешься подключать прокси, особенно соксы.
И самое основное — то что многопоточность тут тоже довольно условна — потоки пускаются по очереди (for) и обрабатываются тоже (while). Вот этот момент бы обойти ))
Условно, но продуктивно)