Как стать автором
Обновить

Комментарии 23

из 1 входящего запроса на мой вебсайт “вырастают” под 200 запросов на API разных грузоперевозчиков
я в похожей ситуации применил отдельный процесс на nodejs, он под такие задачи лучше подходит.
Я о nodejs практически ничего не знаю, но уверен, что существуют инструменты, которыми делать подобные вещи удобнее, чем старый добрый php. Мне нужно было побыстрому закрыть проблему, поэтому я слишком широко не смотрел на проблему.
Честно, не стал читать полностью, но похоже ваша проблема решается через cURL mutli handle

Помимо всего прочего, вам скорее всего ещё будет интересен HTTP Pipeline a.k.a Keep-Alive, который удобно реализовать для PHP через HTTP 1.1 [nginx] reverse proxy на той же машине.
Понял за что минус, действительно я перелистнул упоминание автора об этой фиче. но для конкретной проблемы с 200 запросами из одного своего это подходит лучше. Логика получается достаточно простая, а вот запуск ведомых процессов из PHP запроса — это скорей путь к катастрофе в контексте планирования и контроля ресурсов.

  1. Пройти по кэшу
  2. Взять глобальный lock (!)
  3. Пройти по кэшу ещё раз (!)
  4. Спланировать запросы на недостающую информацию
  5. Подождать и обработать ответы.
  6. Запихнуть в кэш
  7. Отпустить глобальный lock
  8. Отдать ответы.

Примечание: глобальный lock зависит от специфики данных и может зависеть от параметров запроса. В технологическом стеке PHP хорошо подойдёт redis distlock, хитрый лок с транзакцией на базе данных или банальный flock, если сервер только один.
Спасибо за довольно подробный анализ!
Остается вопрос почему не взять готовое решение, коих уже куча? Они ведь уже делали такой же анализ до вас!
Вот навскидку https://github.com/symfony/process/blob/master/Process.php
Я их не нашел, видел какие-то решения на culr_multi_exec(), которые внушали 0 доверия. С Simfony я не знаком. У меня не было так много времени, и мне нужно было решить задачу не навека, а всего лишь кое-как. Если тот вебсайт пойдет вверх, то естественно я сам при первой же возможности перепишу свою асинхронность, а пока текущее решение справляется с возложенными на него обязанностями и масштабируется приемлимо для меня.
решения на culr_multi_exec()

Решение на culr_multi_exec не очень плохо если обращения происходят к одному сайту. Но если надо получить результаты с разных доменов то он может сильно тормозить на процедуре определения ip адреса запрашиваемого сайта.
Мне как то надо было прочитать с миллиона доменов по одной странице и это через culr_multi_exec было очень медленно. Потому что операция "Resolve hostname to IP address" не параллелится.
Запуск множества процессов php тоже не очень привлекательная мысль если нужно делать десятки тысяч запросов.
Об этом я даже не знал. Я у себя в конечном итоге остановился на следующем варианте: создавать дочерние curl процессы (не php и внутри него вызывать curl, а сразу "curl http://google.com"). Так у меня на каждый подпроцесс уходит 600 кб ОЗУ. Я с пхп спрыгнул на нативный curl именно для более оптимальной памяти.
Я тоже остановился на на запуске внешних curl, правда у меня они занимают по 168К.
Но я их запускаю как фоновые процессы, а не как процессы php.
пхп создан чтобы умирать ))
как и люди в прочем.
Знатный костыль. Так делать не стоит. И похоже присутствует путаница между понятиями асинхронности и параллельности. Для асинхронных задач на php есть такое решение — http://reactphp.org/
Межпроцессорная коммуникация — redis, rabbitMq, beanstalkd, gearman — много их. Через stdin\stdout то же хорошо, но не так удобно.

Вообще-то, я считаю, что в 99% случаев оно не надо (и заметьте, это пишет автор статьи на тему параллелизации). Я проработал 8 лет с PHP и до прошлой недели всегда считал большой глупостью пытаться вкрутить многопоточность в PHP.

Ну и 8 лет сидеть на одном php сам по себе плохой выбор, для приведённой задачи можно было выбрать инструменты эффективнее как на самом php (я привёл их выше), так и абсолютно другой ЯП. В моей практики задачи где просто необходимо асинхронное или параллельное выполнение кода возникают на каждом втором шагу.
Спасибо за советы :) Я изначально решил написать статью именно для того, чтобы узнать мнения и советы умных людей из этой темы. Когда текущее решение не будет справляться и нагрузка на эту часть будет расти, то я буду смотреть в сторону технологий, которые вы назвали.
хамелион
Ну, нет.
Поздно, но исправил ошибку. Я почему-то не додумался перепроверить текст рисунка перед публикацией.
там есть ещё «одевать» вместо «надевать» (
Делал как-то тестовое задание, надо было реализовать параллельное вычисление числа pi методом Монте-Карло. Тоже сделал через proc_open(). Одним из условий была работоспособность на любой ОС, поэтому коммуникацию сделал через файлы. Результат можно посмотреть тут, может кому пригодится.
Судя по всему вы просто не очень технологично подошли к решению вашей проблемы. Думаю, намного более эффективно было бы просто складывать все номера на обработку в очередь, а её уже молотил бы отдельный процесс. Если правда то, что процесс создается просто, чтобы висеть и ждать, то это чрезвычайно расточительно. Судя по перечисленным "возможностям параллельного исполнения", для эффективной реализации, лучше было бы взять другой инструмент. В java, например, есть неблокирующий io, который позволяет делать обработку большого числа соединений в одном потоке, при этом никому не надо будет ждать.
Я изначально понимал, что пхп предоставляет мало инструментария на эту тему. У меня задача была по-быстрому слепить решение, чтобы ответ моего пхп кода не занимал дольше 1 минуты. Я понимаю, что мое текущее решение обладает большим количеством недочетов, но на данном этапе оно закрывает все мои потребности (ответ получается генерировать приемлимо быстро, по ОЗУ оно масштабируется в рамках приемлимого, и у моего решения нет внешних зависимостей). Вебсайт, на котором это все крутится, находится на этапе прототипа, и я просто не готов был туда писать красивое решение проблемы сразу. Если я в какой-то момент почувствую, что эта часть кода становится узким местом, то я вернусь к нему и тогда уже подойду серьезно к задаче. Хехе, и тогда как раз и последую советам вашим и других людей, которые упоминали технологии и архитектуры (с этой целью я и писал статью — прощупать почву у людей о том, как они бы решали эту проблему).
Представляю себе, что будет, когда вы увидите, что такое goroutine в go :). Процессы в Erlang, наверное, тоже должны быть ничего.
На самом деле proc_open() тоже работает через системный вызов fork() в UNIX нет способа создать дочерний процесс без этого вызова. Все остальные варианты это просто обертки вокруг fork()/exec().
Но все равно на мой взгляд прекрасное решение в описанных условиях, что бы не говорили об этом другие коллеги )
Не забывайте про POSIX Spawn API.
При разработке сервиса загрузки видео с разных видео-сервисов я это делал так:
  • Скрипт генерит некоторый SID
  • далее Скрипт передает некоторые данные в очередь + SID
  • HTTP страница по AJAX опрашивает готовность задания по SID, а в это время HTTP страница показывает Клиенту некоторую анимашку
  • Скрипт выполнения задач читает данные из очереди, выполняет задачу
  • После выполнения задач, кладет выходные данные в кеш с ключом SID
  • AJAX Скрипт опроса по SID вытаскивает из кеша данные, удаляет их и возвращает Пользователю
  • Счастливый пользователь видит результаты работы долгоиграющего скрипта


Понимаю, что поздно, но может кто и решит использовать такую схему.

Что касается асинхронности в ПХП — тут очень философский вопрос, так что надо использовать те инструменты, которые больше для этого предназначены.

Я работал в рекламе, там большинство бэкграундовских сервисов написано на GO, с использованием горутин
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории