Я пишу интерфейс, в котором я должен запустить 4 http-запроса, чтобы получить некоторую информацию.
Я реализовал интерфейс двумя способами:
Я сравнивал 2 версии с jmeter. Результат показывает, что многократный завиток намного лучше, чем последовательный file_get_contents, когда в jmeter есть только 1 поток, но намного хуже, когда 100 потоков.
Вопрос в том, что может привести к плохой работе с несколькими завитками?
Мой мультиколонный код выглядит следующим образом:
$curl_handle_arr = array (); $master = curl_multi_init(); foreach ( $call_url_arr as $key => $url ) { $curl_handle = curl_init( $url ); $curl_handle_arr [$key] = $curl_handle; curl_setopt( $curl_handle , CURLOPT_RETURNTRANSFER , true ); curl_setopt( $curl_handle , CURLOPT_POST , true ); curl_setopt( $curl_handle , CURLOPT_POSTFIELDS , http_build_query( $params_arr [$key] ) ); curl_multi_add_handle( $master , $curl_handle ); } $running = null; $mrc = null; do { $mrc = curl_multi_exec( $master , $running ); } while ( $mrc == CURLM_CALL_MULTI_PERFORM ); while ( $running && $mrc == CURLM_OK ) { if (curl_multi_select( $master ) != - 1) { do { $mrc = curl_multi_exec( $master , $running ); } while ( $mrc == CURLM_CALL_MULTI_PERFORM ); } } foreach ( $call_url_arr as $key => $url ) { $curl_handle = $curl_handle_arr [$key]; if (curl_error( $curl_handle ) == '') { $result_str_arr [$key] = curl_multi_getcontent( $curl_handle ); } curl_multi_remove_handle( $master , $curl_handle ); } curl_multi_close( $master );
curl_multi_select
не удалось. while (true) { }
петель . CURLM_CALL_MULTI_PERFORM
больше не появляется. Итак, следующий код
$running = null; $mrc = null; do { $mrc = curl_multi_exec( $master , $running ); } while ( $mrc == CURLM_CALL_MULTI_PERFORM ); while ( $running && $mrc == CURLM_OK ) { if (curl_multi_select( $master ) != - 1) { do { $mrc = curl_multi_exec( $master , $running ); } while ( $mrc == CURLM_CALL_MULTI_PERFORM ); } }
должно быть
curl_multi_exec($master, $running); do { if (curl_multi_select($master, 99) === -1) { usleep(2500); continue; } curl_multi_exec($master, $running); } while ($running);
Значение таймаута curl_multi_select
должно быть настроено только в том случае, если вы хотите сделать что-то вроде …
curl_multi_exec($master, $running); do { if (curl_multi_select($master, $TIMEOUT) === -1) { usleep(2500); continue; } curl_multi_exec($master, $running); while ($info = curl_multi_info_read($master)) { /* Do something with $info */ } } while ($running);
В противном случае значение должно быть слишком большим.
(Однако PHP_INT_MAX
слишком велик, libcurl рассматривает его как недопустимое значение.)
Я проверил использование моей параллельной библиотеки исполнителей cURL: mpyw / co
(Подготовка не подходит, и это должно быть, извините за мой плохой английский xD)
<?php require 'vendor/autoload.php'; use mpyw\Co\Co; function four_sequencial_requests_for_one_hundread_people() { for ($i = 0; $i < 100; ++$i) { $tasks[] = function () use ($i) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'example.com', CURLOPT_FORBID_REUSE => true, CURLOPT_RETURNTRANSFER => true, ]); for ($j = 0; $j < 4; ++$j) { yield $ch; } }; } $start = microtime(true); yield $tasks; $end = microtime(true); printf("Time of %s: %.2f sec\n", __FUNCTION__, $end - $start); } function requests_for_four_hundreds_people() { for ($i = 0; $i < 400; ++$i) { $tasks[] = function () use ($i) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'example.com', CURLOPT_FORBID_REUSE => true, CURLOPT_RETURNTRANSFER => true, ]); yield $ch; }; } $start = microtime(true); yield $tasks; $end = microtime(true); printf("Time of %s: %.2f sec\n", __FUNCTION__, $end - $start); } Co::wait(four_sequencial_requests_for_one_hundread_people(), [ 'concurrency' => 0, // Zero means unlimited ]); Co::wait(requests_for_four_hundreds_people(), [ 'concurrency' => 0, // Zero means unlimited ]);
Я пробовал пять раз, чтобы получить следующие результаты:
Я также попытался в обратном порядке (третий запрос был пнут xD):
Эти результаты представляют собой слишком много параллельных TCP-соединений, фактически снижающих пропускную способность .
Если вы хотите оптимизировать как для нескольких, так и для нескольких одновременных запросов, вам может помочь следующее грязное решение.
apcu_add
/ apcu_fetch
/ apcu_delete
. CURLMOPT_PIPELINING
поможет вам. Этот параметр объединяет все соединения HTTP / 1.1 для одного и того же адресата в одно TCP-соединение.
curl_multi_setopt($master, CURLMOPT_PIPELINING, 1);