2014-02-02 2 views
2

Я выполняю HTTP-запросы от моей программы на C++ до своего PHP-скрипта с помощью libcurl.
Первая версия easy_ ниже работает хорошо, однако она довольно медленная (12 запросов в секунду на localhost).
Ничего странного - я получил аналогичные результаты, используя ab -n 1000 -c 1.
С другой стороны, ab -n 1000 -c 100 работает намного лучше с 600 запросами в секунду.
Дело в том, что использование libcurl multi не похоже на одновременное. Я использовал только слегка модифицированный пример кода, и результат также составляет около 12 req/s.
Нет разницы между curl_easy и curl_multi

Я правильно понимаю curl_multi? Как я могу достичь результатов, похожих на ab?
PS. Я знаю, что оба кода немного отличаются друг от друга, однако почти все время тратится на завиток.

easy_ путь:

CURL *curl; 
CURLcode response;    // HTTP response 

curl = curl_easy_init(); 

if(curl) 
{ 


    curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/process.php"); 

    while(true) 
    { 
     if(!requestsQueue.empty())    
     { 
      mtx.lock(); 
      string data = requestsQueue.front();            
      requestsQueue.pop(); 
      mtx.unlock(); 

      const char *post = data.c_str();         //convert string to char used by CURL 

      curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);     

      do 
      { 
       response = curl_easy_perform(curl); 
      } while(response != CURLE_OK); 

     } 
     else 
     { 
      //there are no request to perform, so wait for them 
      cout << "Sleeping...\n"; 
      sleep(2); 
      continue; 
     } 
    } 

    //curl_easy_cleanup(curl);   
} 
else 
{ 
    cout << "CURL init failed!\n"; 
} 

Мульти путь:

CURLM *multi_handle; 
int still_running; /* keep number of running handles */ 

/* init a multi stack */ 
multi_handle = curl_multi_init(); 

/* add the individual transfers */ 
for(int i=1;i<=300;i++) 
{ 
    CURL *handle; 
    handle = curl_easy_init(); 
    curl_easy_setopt(handle, CURLOPT_URL, "http://localhost/process.php"); 
    curl_multi_add_handle(multi_handle, handle); 
} 

/* we start some action by calling perform right away */ 
    curl_multi_perform(multi_handle, &still_running); 

do { 
struct timeval timeout; 
int rc; /* select() return code */ 

fd_set fdread; 
fd_set fdwrite; 
fd_set fdexcep; 
int maxfd = -1; 

long curl_timeo = -1; 

FD_ZERO(&fdread); 
FD_ZERO(&fdwrite); 
FD_ZERO(&fdexcep); 

/* set a suitable timeout to play around with */ 
timeout.tv_sec = 1; 
timeout.tv_usec = 0; 

curl_multi_timeout(multi_handle, &curl_timeo); 
if(curl_timeo >= 0) { 
    timeout.tv_sec = curl_timeo/1000; 
    if(timeout.tv_sec > 1) 
    timeout.tv_sec = 1; 
    else 
    timeout.tv_usec = (curl_timeo % 1000) * 1000; 
} 

/* get file descriptors from the transfers */ 
curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); 

/* In a real-world program you OF COURSE check the return code of the 
    function calls. On success, the value of maxfd is guaranteed to be 
    greater or equal than -1. We call select(maxfd + 1, ...), specially in 
    case of (maxfd == -1), we call select(0, ...), which is basically equal 
    to sleep. */ 

rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); 

switch(rc) { 
case -1: 
    /* select error */ 
    break; 
case 0: 
default: 
    /* timeout or readable/writable sockets */ 
    curl_multi_perform(multi_handle, &still_running); 
    break; 
} 
} while(still_running); 

curl_multi_cleanup(multi_handle); 

curl_easy_cleanup(http_handle); 

return 0; 
+0

Я использовал только curl в очень простых настройках, но вы уверены, что 'curl_easy_setopt' - это правильная вещь при использовании' curl_multi ... '? –

+0

@MatsPetersson: да. Обратите внимание, что URL-адрес - это опция простого дескриптора, а не multi. Каждый запрос может быть связан с разными URL-адресами. – user2461440

+0

@ user2461440 не называть 'mutex.lock' и' mutex.unlock' напрямую использовать блок и ['std :: lock_guard'] (http://en.cppreference.com/w/cpp/thread/lock_guard) быть безопасным исключением. – Mgetz

ответ

1

curl_multi действительно работает с любым количеством передач параллельно, но это не так, используя тот же сингл нить для всей работы. Это имеет побочный эффект: если что-то в любом месте занимает много времени, это действие блокирует все другие передачи.

Одним из примеров такой операции блокировки, которая иногда является чем-то вроде того, что вы описываете, является фазой распознавателя имен. В типичной системе * nix решение libcurl по умолчанию - это стандартная блокировка, которая имеет этот недостаток.

Однако вы можете создать libcurl вместо использования c-ares или backed-resolver-бэкендов, которые избегают этого поведения блокировки и вместо этого намного лучше разрешают параллелизм.

+0

это также применимо к локальным соединениям, что является моим делом? – user2461440

+0

Ах, небрежное чтение меня. Возможно нет. Локальное «разрешение», скорее всего, будет очень быстрым, но все зависит от вашей конфигурации. Попробуйте использовать простые IP-номера для теста и посмотрите, есть ли разница в скорости. –

+0

это просто стандартный debian 7. Я проверю его в данный момент. – user2461440

Смежные вопросы