Правильный способ повторного использования соединений libcurl в многопоточном случае - PullRequest
0 голосов
/ 19 марта 2019

Я хотел бы реализовать систему уведомлений HTTP, используя libcurl . В основном у меня есть пул рабочих потоков и очередь уведомлений. Когда уведомление помещается в очередь, свободный сотрудник в пуле берет его и отправляет его получателю (хост, ip и URL-путь являются параметрами уведомления).

Моя первая реализация основана на curl easy interface . Итак, в основном каждый работник запускает следующий код (упрощенная версия):

static void* workerFunc(...) {
  CURL* curlHandler = curl_easy_init();
  for (;;) {
     // Takes a notification from the queue
     // Use curlHandler to send it, e.g:
     //
     // curl_easy_setopt(curlHandler , CURLOPT_URL, <notification URL>);
     // curl_easy_setopt(curlHandler , CURLOPT_POSTFIELDS, <notification payload>);
     // etc.
     //
     // CURLcode res = curl_easy_perform(curlHandler);
  }  
}

Пока все хорошо. Этот код работает.

Однако у него есть проблема: объект curlHandler является локальным для каждого рабочего потока, поэтому мы не используем преимущества повторного использования соединения, выполняемого libcurl под капотом (по умолчанию libcurl хранит кеш из 5 соединений на обработчик ). Это может быть серьезной проблемой, когда нагрузка на систему (то есть опубликованные уведомления в очереди) высока, так как мы можем закончить слишком большим количеством параллельных соединений, открытых к одному и тому же месту назначения, вместо того, чтобы повторно их использовать.

В худшем случае давайте представим, что все уведомления имеют одно и то же назначение (не говоря уже example.com:9999), например, для пула из 400 рабочих. В сценарии с высокой нагрузкой (все работники, отправляющие уведомления параллельно), мы можем одновременно получить 400 x 5 = 2000 подключений к example.com:9000!

Моей первой идеей для решения этой проблемы было сделать объект curlHandler глобальным. Таким образом, мы могли бы использовать один и тот же пул соединений libcurl для всех работников. Однако я предполагаю, что это не сработает из-за многопоточности (то есть два потока сталкиваются при curl_easy_setopt(curlHandler , CURLOPT_URL, <notification URL>)), если я не реализую механизм защиты на основе мьютекса / семафора.

Работа с mutext / sempahores в собственном коде всегда трудна, поэтому мне интересно, будет ли проще какое-то другое решение (возможно, реализованное на уровне libcurl itslef). Я посмотрел документацию по licurl, которая довольно хороша для справки отдельных функций и структуры данных, но я не нашел конкретного описания для подобного случая (может быть, простой интерфейс curl не может быть использован в этом случае?)

Известно, как другие люди сталкиваются с такой же проблемой и какие решения могут быть применены, было бы здорово.

Спасибо!

...