APPSFLYER DEVCONNECT. 26.07 Olympic Hall
×Закрыть

Когда PHP не справляется: практический опыт перехода на Hacklang

В рамках iForum 2017 года сооснователь и СТО сервиса для планирования путешествий TripMyDream Тарас Полищук рассказал, как и для чего они с коллегами перевели codebase проекта с PHP на Hack. Для читателей DOU, которые не присутствовали на конференции, Тарас изложил этот опыт в авторской статье.

В 2015 году мы запустили beta-версию TripMyDream — сервиса для поиска путешествий без агентств. Суть продукта — подбор выгодных комбинаций «перелет плюс отель» под бюджет пользователя. На первый взгляд задача не выглядит как rocket science, но на деле реализовать ее очень сложно. В этом нам помог язык Hacklang. Но прежде чем рассказывать о нем, объясню, какие задачи мы решали и почему искали именно такой язык.

Предыстория: какие задачи решал TripMyDream

Итак, мы начали тестировать идею и бизнес-модель, запустили beta-тестирование продукта. Много трафика привлекать не планировали, но сервис заинтересовал СМИ, и после ряда публикаций посетители стали заходить на сайт активным потоком. Посещаемость была такой, что каждый день в пиковые часы нагрузка на 8 серверах держалась на уровне 500%. Чтобы понять причины перегрузок, нужно немного углубиться в логику работы сервиса.

Представим условного пользователя, который зашел на сайт, ввел свои требования и нажал кнопку «Найти». В то время задача одного такого поиска сводилась к асинхронному опросу целого набора внешних API, которые предоставляли информацию о наличии авиабилетов, об отелях в нужном регионе, об актуальных ценах. Параллельно шло комбинирование полученных данных с нашей собственной базой прогнозов цен, в которой уже тогда было около миллиарда записей. Все это нужно было совместить и выдать пользователю в виде списка комбинаций «перелет плюс отель», подходящих под его условия. Отдельным вызовом была скорость: мы поставили цель отдавать результат настолько быстро, насколько ответит первая цепочка API-запросов.

В такой ситуации на наши серверы приходило очень много трафика и ресурсы быстро заканчивались. PHP-процессы просто висели в ожидании ответов внешних API, а количество таких процессов является ограниченным.

Почему не справился PHP

Сразу скажу, что с помощью PHP можно решить множество задач, но конкретно в нашем случае это было сложно. Разберу по пунктам, что именно не устраивало в PHP.

Отсутствие нативной асинхронности

Вы можете создать ее искусственно, можете прибегнуть к помощи экстеншена pthreads, однако проблему перерасхода серверных ресурсов это не решит. Например, pthreads, создавая новый поток, делает форк самого процесса. В нем создается отдельный поток. При этом вместе с форком клонируются уже используемые процессом серверные ресурсы, а именно — память. То есть формально появляется два потока, и фактически они «съедают» ресурсов как два полноценных процесса.

Для асинхронизации могут быть использованы воркеры, работающие с очередью задач: например, Gearman или RabbitMq. Такой подход существенно улучшает ситуацию, но при этом вы все равно ограничены в количестве потоков. Каждый воркер скоро будет «съедать» по 30 MB оперативной памяти, и даже если взять сервер с 32 GB оперативной памяти, этих ресурсов хватит на сотню воркеров. Увеличивая объем памяти, проблему решить не получится: вы упретесь в процессорное время. Получается, что для выполнения длительных задач на PHP на одном сервере возникает ограничение в 100 параллельных потоков, если говорить про долгие потоки, блокирующие I/O.

Чрезмерный расход памяти

PHP потребляет очень много памяти, это не будет открытием для PHP-разработчиков. Причина в том, что PHP — слабо типизированный язык, который интерпретируется при каждом выполнении. И если второе решается с помощью кэша, то с типизацией все сложнее. При всем желании и качественной разметке кода невозможно достигнуть такого потребления памяти, которое есть у строго типизированных языков.

Когда дело доходит до огромных коллекций данных, которые в PHP можно использовать только с помощью массивов, серверные ресурсы начинают растворяться.

Поиск альтернативы: ответы, которые мы искали

Мы пришли к выводу, что на PHP не получится решить поставленную задачу. Пришлось искать другое решение. При этом нужно было учитывать два важных обстоятельства. Первое: мы — стартап, а значит, не можем потратить пару месяцев на смену стэка. Каждый день — драгоценный. Второе: у нас была команда PHP-разработчиков, и решить задачу предстояло им. Я много общаюсь с нашими партнерами, коллегами, конкурентами, и знаю, что большинство решает подобные задачи либо с помощью node.js, либо с помощью Java. Но для перехода на node.js нам потребовалась бы новая команда из nodejs-девелоперов, т. к. в реальности PHP и node.js — это две параллельные вселенные.

Итак, мы начали искать решение, которое предполагало строгую типизацию, аккуратную работу с памятью и асинхронность, которая не расходовала бы так много ресурсов. У нас была команда из PHP-девелоперов, и она должна была быстро мигрировать на новый язык. При этом PHP7 еще не вышел в свет. Таким решением стал Hacklang.

Знакомство с Hack

Hack, он же Hacklang — это полноценный язык программирования, разработанный компанией Facebook. Начиналось все с трансляторов кода, когда лет пять назад Facebook представил HipHop Virtual Machine (HHVM), а ВКонтакте — транслятор Kitten PHP (kPHP). Спустя два года Facebook зарелизил полноценный язык программирования, разработанный под виртуальную машину HHVM, полностью совместимый с PHP.

Декларируемые цели языка — быстрая разработка без багов, быстрый runtime, масштабируемость, положительный опыт в написании кода. Утверждение о «быстрой разработке без багов» кажется фантастическим, но на деле оно очень даже похоже на правду. Hack предусматривает статический анализ кода, который и помогает избежать 99% ошибок еще до runtime. Я подробнее расскажу о нем позже.

Синтаксис Hack очень похож на PHP. Можно сказать, что Hacklang — это как PHP с дополнительным синтаксическим сахаром и новыми возможностями.

Пять преимуществ в пользу Hacklang

Помимо уже заявленных свойств, Hacklang имеет и другие преимущества, которые оказались крайне важными для нас. Перечислю их.

Типизация

Это была одна из ключевых причин, почему мы мигрировали на Hacklang. Hack подразумевает сильную типизацию по умолчанию, при которой разработчик обязан указывать типы декларируемых переменных, аргументов и результатов выполнения функций.

Примитивные типы полностью пересекаются с PHP, за исключением типа void, который позволяет в коде просто написать return с точкой с запятой. При этом в Hack есть другие типы: mixed, nullable, arraykey, num и noreturn. Внимательные PHP-девелоперы могут возразить, что в PHP всегда был тип mixed, но на самом деле это псевдо-тип, используемый для описания языка.

Акцент в Hack ставится на разработку сильно типизированных и масштабируемых систем, в то время как PHP акцентируется на скорости разработки.

Коллекции

Помимо дополнительных типов, в Hacklang есть нативные коллекции. В большинстве случаев они являются универсальным решением всех проблем. У каждой коллекции имеется свой полноценный интерфейс и набор методов для удобной работы с данными, сортировки, фильтрации и конвертации в другие типы или коллекции.

Map — типичный key-value контейнер, ключ которого может быть строкой или целым числом, а значение — любым.

Set — контейнер уникальных строковых или числовых значений. Например, используя метод add, можно быть уверенным, что в контейнере лежит единственный экземпляр значения.

Vector — самый типичный стэк. По сути, это массив, ключ которого является строго числовым и автоинкрементым.

Pair — контейнер, который содержит строго два значения любых типов.

Дженерики

Те, кто разрабатывает на Java, хорошо знакомы с дженериками — параметризацией вызываемых классов и функций. Тип или несколько типов, которые будут использоваться внутри методов или как тип возвращаемого результата, передаются вместе с объявлением класса или вызова метода. На практике это значит, что вам не нужно наследовать и перегружать методы, если вы хотите работать с несколькими разными типами в рамках одной логики. В Hacklang есть полная поддержка ковариативности и контравариативности.

Встроенный тайпчекер

Одна из самых интересных частей — встроенный тайпчекер, который проводит статический анализ кода еще до его выполнения. Собственно, здесь возвращаемся к заявленной возможности писать быстрый код без багов.

Hack поддерживает три режима. Partial mode позволяет совмещать codebase на PHP и Hack одновременно. Strict mode — это режим строгой типизации всего кода. В этом режиме тайпчекер проверяет абсолютно все файлы проекта и говорит об ошибках. Даже если в одном из сотни файлов будет ошибка, то весь проект в runtime выполняться не будет — идентично со строго типизированными языками. Мы принципиально пишем весь код именно в этом режиме. И, наконец, режим declarative mode полностью отключает тайпчекер.

JIT, just-in-time компиляция

Опять же, те, кто разрабатывает на Java, понимают преимущества JIT. Это компилятор, транслирующий написанный девелопером код в инструкции машинного кода — с рядом последующих оптимизаций. Когда дело доходит до пиковых нагрузок или сложных машинных расчетов, именно JIT обеспечит приложению быструю работу.

Реализация асинхронности в Hacklang

Первой ключевой причиной, по которой мы выбрали Hack, была типизация. Вторая — реализация асинхронности. На ней стоит остановиться подробнее, поэтому я вынес этот вопрос в отдельный раздел.

Следует понимать, что это не многопоточная асинхронность, а однопоточная, так называемая однопоточная многозадачность. Работая в едином потоке, Hack позволяет запускать ряд задач параллельно. Для этой цели служит встроенный планировщик. Кроме того, между этими параллельными задачами можно переключаться. Используется парадигма async/await, которая позволяет декларировать функции асинхронными (с помощью async) и получать указатель на их выполнение (с помощью await). Разберемся, как это работает.

Существует ряд неблокирующих компонентов, встроенных в язык: AsyncMysql, Memcached, cUrl и потоки. Под определением «неблокирующие» имеется в виду время ожидания результата, время простоя, которое может быть использовано для выполнения других задач.

На практике это выглядит так. Когда с помощью cURL вы опрашиваете какой-то URL, то ждете ответа, допустим, 10 секунд. Или же отправляете запрос в MySQL — и ждете выборки 2 секунды. Так вот, Hack позволяет не ждать, а в это время идти дальше по потоку — к другим асинхронным задачам. То есть, эти задачи можно запустить вместе и результат получить вместе с последней функцией. Не нужно ждать завершения работы одной функции, чтобы начать другую.

Для примера приведу задачу, которую легко решить на Javascript — с помощью обычных коллбеков — и достаточно сложно решить на PHP. Она нестандартная, но хорошо демонстрирует возможности Hacklang. Итак, допустим, необходимо каждую секунду отправлять параллельно ровно три API-запроса. Представим, что мы платим деньги за неограниченный доступ, но имеем всего три запроса в секунду. Четыре запроса в секунду отправить нельзя: это блокирует запросы следующей секунды. При получении ответа результат необходимо обрабатывать и сохранять. Время ответа API — от 0,5 до 5 секунд. И все должно работать в едином потоке одного процесса.

Как же это решается на Hack? На минуту вперед создается массив со слотами для async-функций каждого запроса — и запускается одновременно. На каждую секунду мы имеем по 3 функции. Итого, 180 слотов для выполнения функций. Каждая async-функция запускает асинхронный cURL со своим заданным callback. В каждой функции есть проверка, настало ли ее время выполнения. Когда планировщик попадает в async-функцию, время которой еще не пришло, он ставит её выполнение в конец очереди. Плюс небольшой слип, чтобы не перегружать процессор лишними циклами.

Дополнительные возможности для разработчика на Hack

Hacklang имеет немало дополнительных возможностей. Я опишу в общих чертах инструменты и «фишки», которые нахожу полезными, а примеры их использования можно найти, к примеру, в документации HHVM или в моей презентации для iForum

Тип shape

Shape — это специальный тип, объединяющий 0 и больше полей как единое целое. Полная валидация тайпчекером происходит еще до runtime. По сути, тип shape дает возможность не создавать отдельный класс под микромодель данных.

Мемоизация

Над функцией ставится мета поле Memoize, и виртуальная машина кэширует ответ функции с конкретными заданными аргументами. При последующем обращении она выдаст ответ без выполнения функции. Это мелочь, которая позволяет не строить велосипеды там, где это будет избыточно.

Constructor parameter promotion

Объявление свойств класса можно реализовать с помощью аргументов в конструкторе. Достаточно добавить к аргументам конструктора их видимость (private, protected, public).

Операторы Lambda, Null-safe, Pipe

Оператор Lambda ( ==>) для лямбда-функций. Помимо синтаксического сахара, он дает возможность использовать текущий scope в лямбда функциях без использования use.

Оператор Null-safe (?->) позволяет избежать дополнительных проверок на null-ность объекта, предоставляя возможность безопасного доступа к методам.

Оператор Pipe (|>) — синтаксический сахар, позволяющий избежать избыточного вложения вызовов функций.

Режим Repo Authoritative

Стандартно HHVM ведет себя так же, как PHP-движок: загружает и компилирует ход на лету. Режим Repo Authoritative позволяет сразу скомпилировать весь код в бинарный репозиторий, что сильно ускоряет работу на пиковых нагрузках.

Типы серверов FastCGI и Proxygen

В HHVM можно использовать два типа серверов: FastCGI и Proxygen. В первом случае мы видим стандартное использование, схожее с nginx + php-fastcgi. HHVM запускается независимо от web-серверов (apache, nginx и других). Во втором варианте виртуальная машина HHVM имеет встроенный полноценный web-сервер, который позволяет принимать запросы напрямую, исключая промежуточный web-сервер (например, nginx).

Встроенный шаблонизатор

В Hack разработан полноценный шаблонизатор XHP, который предоставляет нативную XML-репрезентацию для HTML-кода. HTML-код проверяется тайпчекером, и HTML с ошибками не позволит скомпилировать код.

IDE, фреймворк и другие «организационные моменты»

Когда мы начинали писать на Hack, еще не существовало полноценного IDE. Мы использовали phpStorm и допиливали в нем распознование Hack как PHP. Сейчас все стало гораздо проще: для знакомого многим компонентного IDE Atom существует компонент Nuclide, созданный Facebook. Он применяется для разработки на ряде языков, включая Hack. Безграничные возможности самого Atom превращают разработку на Hack в приятное времяпрепровождение.

Сейчас в числе слабых сторон можно назвать то, что в Hack отсутствует менеджер расширений и возможность устанавливать внешние экстеншены как таковые. При этом можно добавлять любые PHP-библиотеки через composer или совершать полуавтоматическую конвертацию в Hack-код. Собственно, мы так поступали с клиентами для Google Adwords, Mailchimp, Mandrill. Еще есть вариант скомпилировать свой код на C++ вместе с виртуальной машиной HHVM.

Еще один недостаток — отсутствие полноценного Framework. Если нужно написать плюс-минус стандартизированное MVC-решение, лучше использовать PHP с Yii2/Symfony. Если же вы хотите построить сервис или микросервис, где логика фреймворка заканчивается на роутере и коннекторах, стоит попробовать свои силы в написании на чистом Hack в strict mode. В partial mode вы можете смело совмещать и Hack, и PHP код.

Экосистема Hacklang не такая огромная, как у PHP, но она постоянно развивается. Еще два года назад документация была очень слабая, по многим пунктам просто перекидывала на документацию PHP. Сейчас подобных проблем нет. Можно рассчитывать на полноценный релиз каждые 8 недель, ночные сборки, хороший фидбек. Например, если вы нашли какой-то issue и запостили его в GitHub-репозитории Hack, ответ от одного из девелоперов, скорее всего, появится в течение суток. Если найти что-то стоящее и предложить коммит — его с радостью примут.

Полезные ресурсы: активный GitHub, 280 вопросов на Stackoverflow, официальная документация.

Что касается установки, для Ubuntu, Debian есть установка из готовых пакетов и возможность скомпилировать исходники с GIT. Для OSX — установка через brew и, опять же, возможность скомпилировать исходники с GIT. В случае с OSX бывают сложности установки (если делать это нативно без Docker), однако они длятся недолго, релизы с исправлениям выходят с постоянной частотой. Для других Linux-системы тоже возможно скомпилировать исходники. Существуют образы в Docker Hub, которые можно использовать для контейнеризации HHVM. Для Windows установка пакета пока невозможна, в разработке портирование с помощью MSVC.

Напоследок скажу о миграции. Если вы задумались о переходе на чистый Hack (без использования legacy php кода), то, опираясь на опыт TripMyDream, вам скорее всего придется писать код с нуля. Также для Hack существуют инструменты автомиграции кода из PHP в Hack, однако, используя их, вы можете не ощутить всех преимуществ разработки на чистом Hack. Для разработчиков процесс миграции происходит довольно легко. Уже через несколько недель PHP Developer свободно пишет на Hack, а совсем скоро он проектирует и пишет код полноценно, с измененной парадигмой.

51 комментарий

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

можна ж було просто зробити окремий мікросервіс для curl реквестів на будь-якій норм мові програмування (наприклад golang).

дивні у вас домени. com для росії, en.....com для англ і .ua для укр.
я б поставив англ на com, uk....com для україни і ru....com для росії. набагато логічніше і краще для рейтингу домену.

Гы, кто-то действительно этим пользуется кроме нас :)

Slack тоже использует Hack, судя по посту их Chief Architect ) www.facebook.com/...​ermalink/856809177805810

PHP

закрываю вкладку

и побыстрее, тебя сюда не звали

ну извините что задел чувства тех кто еще юзает это дерьмо на костылях :)

дерьмо у вас в голове, уж извините :)
а чувства вы не чьи не задели, не льстите себе, слишком много значимости себе не приписывайте)

если бы PHP был норм то не пришлось бы привинчивать к нему эти костыли))

Если бы вы были норм — не писали бы подобный бред, а понимали, что каждому языку — своё применение.

каждому языку — своё применение.

сказки для школьников, просто перспективные и хорошие технологии выдавливают с рынка технологии, которые свое отжили или изначально были херово спроектированы. А кто-то кому лень или дорого переписывать проект — привинчивает костыли и пишет об этом статью вместо того что бы предостеречь людей:)

Где-то в глубине инженерной мысли я с вами согласен. Однако правила диктует бизнес, PHP занимает огромную долю рынка разработки, и имеет конкретные преимущества как в скорости разработки, так и в затратах на разработку. Вот альтернативное мнение — stackoverflow.com/a/7902620 . Что позволит валидировать идею, от чего легче отказаться, если идея не работает, что даст быстрый time to market? Супер оптимизированная ракета на C++ за пол года и $XXX или самокат на PHP за 2 недели и $X ?

php как раз никто не куда не выдавил, имеет свою объемную долю рынка, развивается активно, это показатель. Может вы просто не умеете готовить?

Зачем вообще открывал? :)

його настільки не хвилює PHP що він не міг пройти повз)

Понравились слова

PHP не справляется

вірно, комплекси треба ж підкормлювати

я не автор заголовка ;)

У нас 200 воркеров на сервер съедают 5-7G оперативки (php5.6, RabbitMQ, 10k/s толстых json).
По загрузке CPU — претензий нет, да и +1 железка — 30-60$.

Что будете делать, когда Hacklang утратит популярность/поддержку/устареет через 3-5 лет, как сейчас HHVM?

Хороший вопрос, и про HHVM тоже правда. Пока нет единого мнения на этот счет. Скажем так, мысли такие регулярно приходят, но лучшего решения для текущего стэка, с точки зрения целесообразности миграции, пока не могу найти. Рано или поздно, всё мигрирует на микросервисы и server-less решения. А там уже без разницы, что на чем.

всё мигрирует на микросервисы и server-less решения
А там уже без разницы, что на чем.

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

Не понял, но понравилось.

Отсутствие нативной асинхронности
Вы можете создать ее искусственно, можете прибегнуть к помощи экстеншена pthreads

насчем с того, что pthreads никакого отношения к асинхронности не имеет, а так же того, что следуюет отличать асинхронность и параллельность.

Например, pthreads, создавая новый поток, делает форк самого процесса. В нем создается отдельный поток. При этом вместе с форком клонируются уже используемые процессом серверные ресурсы, а именно — память. То есть формально появляется два потока, и фактически они «съедают» ресурсов как два полноценных процесса.

боже, как всё плохо. pthreads не форкает процессы. pthreads никакого вообще отношения к процессам не имеет. вы несете херню, а я читаю ее со слезами на глазах.

когда вы создаете тред, создается один тред.
когда вы запускаете пулл с воркерами, то максимум, что создастся это количество воркеров по размеру пула + количество задач по размеру пула.

$pool = new Pool(32);
while (true) {
    $pool->submit(new Task());
}
доведет количество тредов до 64 и всё.

Каждый воркер скоро будет «съедать» по 30 MB оперативной памяти, и даже если взять сервер с 32 GB оперативной памяти, этих ресурсов хватит на сотню воркеров.

php жрет крайне мало памяти, сравнивая с той же node.js.
да о чем вообще речь, я вращал на 8гб ram пул на 1024 воркера (итого 2048 потоков вместе с задачами).

Увеличивая объем памяти, проблему решить не получится: вы упретесь в процессорное время. Получается, что для выполнения длительных задач на PHP на одном сервере возникает ограничение в 100 параллельных потоков, если говорить про долгие потоки, блокирующие I/O.

не получается. потоки с блокирующими задачами (например, запросы к другим серверам) тупо висят в памяти и ждут, пока придут данные. и уж точно не отъедают процессорное время.

PHP потребляет очень много памяти, это не будет открытием для PHP-разработчиков.

очень много в сравнении с чем?
пустой процесс node.js жрет ~40-50мб, консольный helloworld на c# жрет ~30мб.
helloworld на php будет кушать ~5мб.

Когда дело доходит до огромных коллекций данных, которые в PHP можно использовать только с помощью массивов

SplDoublyLinkedList, SplStack, SplQueue, SplHeap, SplMaxHeap, SplMinHeap, SplPriorityQueue, SplFixedArray, SplObjectStorage. не?



скорей всего ваша задача состояла в том, чтоб собрать данные с нескольких серверов и выдать пользователю, для чего даже pthreads нахрен не нужен. хватило бы неблокирующих сокетов (о которых вы, похоже, не слышали) для которых в php есть два прекрасных core extensions, для которых есть куча врапперов в виде того же reactphp.
да что уж там, ниже справедливо подметили про асинхронный curl.

подскажите, вы теоретик или практик?

я вращал на 8гб ram пул на 1024 воркера (итого 2048 потоков вместе с задачами).

очевидно, что практик.

Хорошо. По Spl я говорил в докладе, этого просто нет в статье.

По поводу потребления памяти в php пример с Hello world, к сожалению, не подходит. Представьте, что нужно в одном пользовательском поиске распарсить 500-800 мегабайт (это не опечатка) данных из полученных извне json в оптимальные коллекции данных, и потом где-то это закешировать. На все есть 1-3 секунды. Сколько потребуется памяти php на всю задачу (overhead к данным)?

Мы с вами обсуждаем разный pthreads (github.com/krakjoe/pthreads)

ну всегда можно взять что-то такое, например, github.com/...​lsify/jsonstreamingparser и писать сразу в кеш.

Мы с вами обсуждаем разный pthreads (github.com/krakjoe/pthreads)

pthreads в php один. и пусть мы говорим о разных версиях (вы о 2.*, я о 3.*), тем не менее pthreads не создает процессы и не создавал, чем выгодно отличается от варианта с pcntl_fork().

С удовольствием бы так и сделали, но структура json не потоковая, и требует связей в разных нодах :\

тогда да, обидно.
в php json_decode(file_get_contents(’data.json’)); файла размером в 500мб отожрет где-то так полтора гб.

тем более есть же вещи, типа habrahabr.ru/post/280262 , которые неплохо обгоняют Spl.
хотя да, конкретно этот пример уже, увы, для php7.

Странно. Если за раз система получает 800Мб из вне и обрабатывает за секунду это 8Гбит трафика. Какой у Вас входящий канал Вы говорите ?

а где написано про трансфер по сети за секунду, в слове «распарсить»? входящий канал у нас 40ГБит.

500-800 мегабайт (это не опечатка) данных из полученных извне
На все есть 1-3 секунды.

И насколько загружен входящий канал ?

Не загружен, это канал на одиночный инстанс, а 800MB это пиковый пример. Тут скорее CPU на обработке закончится, чем канал.

консольный helloworld на c# жрет ~30мб

image.prntscr.com/...​3P-ncd-Tmy9kEXLbAsPNw.png
Почти.

это, конечно, очень интересно, но мерять частный рабочий набор — немного не то,
drive.google.com/...​I3_NgKT_J1WXI1WXliVVlUYjg

Понятия не имею в чем Ваша проблема.

Существует Multi Curl php.net/...​ction.curl-multi-init.php — не нужно плодить 20 php процессов (которые у вас почему-то кушают какие-то нереальные 30 МБ) или делать 20 форков. Все запросы выполнятся асинхронно в одном процессе. Из минусов — процессор напрягается.

Я так понимаю PHP7 в то время еще не был зарелизен?

Не был зарелизен — так и сказано «У нас была команда из PHP-девелоперов, и она должна была быстро мигрировать на новый язык. При этом PHP7 еще не вышел в свет. Таким решением стал Hacklang.»

А начали бы вы все это городить, имея PHP7 в теперешнем его состоянии? Вопрос к автору поста, естественно.

Не могу дать истинный ответ :) И да, и нет, и возможно не PHP вовсе.

Multi Curl мы используем по полной программе, в Hack есть еще асинхронный curl_multi_await. Но задача не просто опросить параллельно список end-point-ов, а опросить их в цепочке, каждая из которых имеет свой набор и кол-во последовательных запросов, с передачей состояния от одного к другому. Представьте себе дерево запросов, где есть авторизация, инициализация поиска, опрос, расширенный опрос и т.д., а теперь добавьте несколько таких веток, у каждой из которых будет своя цепочка и логика (например отели одного провайдера, другого, и третьего). В php в один момент времени может выполняться работа только одного Multi Curl дескриптора, таким образом если вы будете в коллбек функциях создавать и выполнять последующие дочерние Multi Curl дескрипторы, то родительский дескриптор будет в это время проставить.

github.com/clue/php-buzz-react

$loop = Factory::create();
$client1 = new Browser($loop);
$client2 = new Browser($loop);

$client1
    ->get($uri1_1)
    ->then(function ($response) use ($client1) {
        var_dump((string)$request->getBody());
        
        return $client1->get($uri1_2);
    })
    ->then(function ($response) use ($client1) {
        var_dump((string)$request->getBody());
    });
    

$client2
    ->get($uri2_1)
    ->then(function ($response) use ($client2) {
        var_dump((string)$request->getBody());
        
        return $client2->get($uri2_2);
    })
    ->then(function ($response) use ($client2) {
        var_dump((string)$request->getBody());
    });
    

$loop->run();

ReactPHP в целом очень крутая штука. С Hacklang его можно совместить в режиме partial, и все работает в смешанном коде.

ну и производительность скорей всего будет примерно как на php7.
вообще жаль, что php7 не успел к вам.
хотя, может, я слишком враждебно отношусь ко всяким KPHP и HHVM.

Спасибо за ответ. Не зная точных требований проекта судить не берусь — но думаю что можно было решить задачу и без Hack. Возможно костыльно, но быстро.

Например складывать все нужные запросы в Redis, запустить демона проходящегося по нужным урлам, делающим запрос и складывающего ответ в Redis обратно. И вот этого демона написать на чем хотите — Node.js, Go etc. Родительский процесс висит и периодически опрашивает Redis, загружает в него новые урлы для продвижения по цепочке.

Абсолютно поддерживаю, мы делали через очереди (Gearman/RabbitMq), и дальше с помощью воркеров. Сейчас смотрим в сторону Kafka + Go. Но Hack мы выбрали не только из-за этой одной задачи, это лишь пример, люди любят истории) В целом, используя все возможности языка (strict режим, дженерики, коллекции и тд) сама организация кода становится ближе к C языках, и с ней реально приятнее работать.

Если необходимо парсить JSON сложной структуры, то на Go даже не смотрите, намучаетесь с Unmarshal.

та много чего существует.
есть тот же libuv. страшный и ужасный, но вполне работает.
есть два варианта неблокирующих сокетов.

Подписаться на комментарии