Pull to refresh

Comments 53

UFO landed and left these words here
Не совсем понятно какие ограничения накладывает использование RoadRunner. На вскидку, не должны использоваться глобальные переменные и объекты, включая разные singleton экземпляры. Как ведут себя в такой среде коннекты до БД, tcp?
Не совсем в тему, у меня есть несколько демонов на чистом PHP, в которых коннект к MySQL, Redis и Memcached. Даже таймауты отдельно не настраивал, все может работать в таком режиме неделями (иногда перезапускаю для обновления).
На всякий случай сделал обработку поведения по Mysql gone away: просто переподключаемся и работаем дальше. На тестах работает, в живую вроде бы не пригождалось.

Тут скорее всего то же самое поведение будет, демон он и есть демон.
За памятью разве что надо следить, а PHP для этого не совсем подходит, впрочем, все решаемо.
Ведут себя нормально, так как со сторны php это не форки а просто выполнение разных запросов в цикле.
Нужно не забывать закрывать коннекты после обработки запроса, либо если используете пул коннектов то учитывать состояние. Чтобы не получилось так что в при обработке запроса вы открыли транзакцию и зафейлили запрос после чего бд с открытой транзакцией ушла обратно в пул.
В целом годная штука, но облать применения довольно узка так как оно не ложится на мышление современных php программистов и на сторонние библиотеки этими программистами написанные.
Поясните, пожалуйста, с одной стороны вы вроде говорите, что состояние получается изолированным для каждого отдельного запроса и иного подхода потребуют, вроде как, только вполне конкретные места, типа взаимодействие с БД, но с другой стороны говорите, что на мышление php программиста всё это плохо ложиться.

Так всё же, в целом, насколько велики отличия классического php подхода к разработке и тем, что предлагает RoadRunner? Если представить некое абстрактное, типичное PHP приложение-сайт, то при переносе его на эту технологию, в каких узлах ожидать проблемы, что вероятно потребует изменений? Хочется понять уровень этих проблем и изменений, это уровень глобальный, архитектурный или всё таки локальный, вроде того что вы описали насчет работы с БД.
с одной стороны вы вроде говорите, что состояние получается изолированным для каждого отдельного запроса и иного подхода потребуют, вроде как, только вполне конкретные места, типа взаимодействие с БД

Где? Вроде не говорил такого.

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

Также следует ожидать утечечек памяти, но я так понимаю этот вопрос фиксится просто переодическим перезапуском воркеров.

Если ваше приложение использует request — response абстракции (например построено на основе symfony) и написано в академично-интерпрайз манере то ваши шансы на переход конечно возрастают, однако с ходу всё равно не заведется — нужны будут правки.
RoadRunner интегрируется посредством добавления цикла с получением PSR-7 запроса, поверх обычного запуска приложения, примерно вот так:

Код воркера
<?php

use Spiral\Goridge\StreamRelay;
use Spiral\RoadRunner\Worker;
use Spiral\RoadRunner\PSR7Client;

use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;

use Illuminate\Http\Request;

require_once(__DIR__ . '/../vendor/autoload.php');

$relay = new StreamRelay(STDIN, STDOUT);
$psr7 = new PSR7Client(new Worker($relay));

$requester = new HttpFoundationFactory();
$responder = new DiactorosFactory();

$app = require_once(__DIR__ . '/../bootstrap/app.php');
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

while ($psr7Request = $psr7->acceptRequest()) {

    try {

        $request = Request::createFromBase(
            $requester->createRequest($psr7Request)
        );

        $response = $kernel->handle($request);
        $kernel->terminate($request, $response);

        $psr7Response = $responder->createResponse($response);
        $psr7->respond($psr7Response);
		
    } catch (\Throwable $e) {
        $psr7->getWorker()->error((string) $e);
    }
}


При таком подходе, постоянно есть ссылка только на инстанс $app и $kernel. Все остальное, что находится вне этих инстансов, сборщик мусора затирает (ибо он затирает все, на что ссылок больше не осталось), в том числе и подключения к БД (если они созданы вне контейнера $app), сокеты и прочие ресурсы.

В теории, если внести создание $app и $kernel внутрь цикла, то сборщик мусора при каждом новом запросе будет чистить абсолютно все.
>В теории, если внести создание $app и $kernel внутрь цикла, то сборщик мусора при каждом новом запросе будет чистить абсолютно все.
Может тогда уж просто вернёмся к умиранию?)
Я думаю, что умирать он будет и так… еще с десяток лет, а может и не один :)
А по существу, то даже так прирост производительности будет, ибо нет создания процесса, подключения файлов и компиляции кода/загрузка из опкэша)

При использовании php-fpm уже давным-давно как нету создания процесса.

Дополню соседний ответ:
в 7.4 планируется подгрузка библиотек при первом запросе и подключение во всех последующих, что убирает ещё 1 пункт из вашего сообщения.

Имеется в виду, что если не использовать статичные и глобальные переменные – не будет сохраняться никакое «лишнее» состояние между запросами. Но зато всё равно будут оставаться в памяти уже загруженные классы, что всё равно даст прирост производительности.

Все же непонятно, зачем было так изголяться. Взяли бы java, kotlin или nodejs (с typescript), и не пришлось бы женить ежа с ужом

Угу, уволить давно подобранные команды и начать с нуля или начать переучивать хороших спецов php на совсем другой стек технологий.
Цена? Смысл?

А вы думаете, что изобретение своего велосипеда будет дешевле? В долгосрочной перспективе — это вообще может привести к коллапсу. Да и очень странно, что хороший специалист с опытом знает только один ЯП.

Мне очень странно от инженеров слышать что-то вроде "возьмем язых Х и это решит все наши задачи". Не решит. Решит какую-то часть лучше, какую-то хуже. Мы инженер, а не пустомели все же, и должны идти от задачи, а не нашей хотелки. Иначе мы просто играем в клевую разработку, но не создаем решение.


Про велосипеды, можно примеры, лучше с коллапсом?


Но заметьте, в статье ровно об этом речь: для части задач php плохо подходил, взяли go, решили задачу лучше.
Я знаю примеры эволюции компании от php к go+php, затем к java, и сейчас к java+go. Но это не значит, что именно связка java+go работает идеально или что ее нужно было брать сразу. Каждый набор инструментов решал свои задачи в свое время, и имел определенную цену, которая или оправдана или нет.
В поиске этих компромиссов, оценке разной стоимости и затем обосновании решения и состоит наше с вами дело. Но, согласитесь, странно заявлять "c#! kotlin! Golang! Java!"

Очень странно — это взять не рунтайм язык и заниматься годами построением костылей, чтобы он стал рунтайм. Плюс взять ЯП, который не умеет создавать треды и обвешивать его другими сервисами и технологиями, чтобы сделать хоть какое-то подобие многопоточности. Да, у меня есть примеры крупных проектов и коллапсов. Если что, то отписал на пыхе, почти 15 лет.

Работал на огромном портале недвижимости, весь написан на пыхе. Очень много работы с географией, кучей парсеров на миллионы записей и прочих прелестей. Сам портал увешан в итоге стал и редисом, и мемкешем, и риаком для хранения изображений, и кучей каких-то демонов, которые проверяют процессы распарсивания данных, пару разных очередей zeroMq и кролик. Плюс еще стоял эластик для поиска и еще куча всего. Времени исполнения некоторых скриптов не хватало, стали запускать в терминале с бесконечным таймингом. Потом стали писать под это свои веб обертки. В итоге пришли к тому, что в один поток работать — это лютый треш. Стали придумывать костыли. Прикрутили какую-то стороннюю сырую либу для пыхи, которая делала вид, что работает с тредами. Потом занимались херней, распараллеливая запросы прямо с веб морды, через сокеты (не нужно ждать ответа от сокета, что позволяет выполнять код в цикле, открывая новые сокеты с задачами). После всего этого зоопарка пришло понимание, что все это обслуживать, а тем более ловить баги — это очень суровая задача. Стали бить на микросервисы. Уперлись в монолитную БД, которую пришлось дробить и организовывать свой SOAP сервис, чтобы весь это зоопарк связать вместе (плюс еще 1С была подключена и своя CRM). Естественно, никто не писал никакой документации, комментариев в коде, так как всем все нужно на вчера. В итоге, через год команда на столько зае… алась со всем этим, что три четверти уволились в течении одной недели. Набрали новых, реально опытных людей. Они туда глянули, поколупались пару недель и тоже свалили. Так как возиться с этой кучей навоза на хрен никому не нужно ни за какие деньги.

Отсюда мораль. Не стоит делать на неподходящей технологии сложные вещи только по тому, что ты хорошо знаешь эту технологию, но не знаешь другую, которая больше всего подходит для конкретной задачи. И вместо того, чтобы реализовывать бизнес логику и развивать БИЗНЕС, все дружно пытаются на… бать ЯП, чтобы он делал то, для чего не предназначен.

Решать конечно каждый должен сам, что ему делать и как он считает правильным. Но лично я с пыхи вообще свалил на фиг, потому что знаю не по наслышке, что такое суровые проекты на пыхе.
Секунду, как вы смогли-то в «монолитную» бд упереться-то?
И да, мне не показалось, что вы предлагаете померяться? Можем, но тогда избавьте окружающих от этого зрелища и переходите в личку. Но если коротко, то проблемы в языке у вас не особо было, максимум, выделили бы эти самые критичные куски и вынесли в отдельные микросервисы, где сможете любой подходящий набор технологий использовать.
И да, однопоточный php отлично себя чувствует в highload, если всеми любимая архитектура была под это поправлена. Многопоточность-то тут причем? Да и «пришло понимание, что» — это какая-то задача не решалась или просто нового захотелось?

Про php, есть опыт работы в e-commerce на 6 стран. Была изначально php 5.4, и большие проблемы в рампродажи, когда нагрузка уходила за 20k rps на checkout. Стали рефакторить этот кусок, чтобы его выкусить в микросервис, затем тоже для импорта товаров, и затем для рассчета акций и скидок. Отрефпкторенный php переносили в гошные микросервисы, переключались на них с php, оставляя ту же БД и постепенно перекрывая доступы к отдельным ее табличкам со стороны php.
В итоге решение заработало, php осталось, весь проект не переписывали, фичи бизнес получал non-stop, поскольку часть ресурсов оставалась на php и кодила дальше. Как подоспела 7я версия, то мигрировали на нее и нагрузка стала переноситься и еще с большим запасом.
Вопрос, зачем бегать кругами и орать, что весь язык плохой? Тем более сгенерировать html php часто умеет вровень или шустрее java, python, go.
Не решалась очень определенная задача «checkout не выдерживает в дни распродаж», ее и решили. Зачем все переписывать? Или зачем страдать об отсутствии (условно) многопоточности в php, когда пишешь генерацию html странички по закэшированным данным?
Очень странно — это взять не рунтайм язык и заниматься годами построением костылей, чтобы он стал рунтайм.
Современный PHP — вполне может работать рантайм, я его напрмер использую для telegram бота как systemd сервис для обработки long polling запросов, вот пруф (картинку лучше открыть в отдельной вкладке): image
Жалко этот сервер в начала декабря перегружал, до этого там несколько месяцев рантайма было.
Плюс взять ЯП, который не умеет создавать треды и обвешивать его другими сервисами и технологиями, чтобы сделать хоть какое-то подобие многопоточности. Да, у меня есть примеры крупных проектов и коллапсов. Если что, то отписал на пыхе, почти 15 лет.
Предполагаю, что вы проработали 15 лет и ушли до PHP 5.3 т.к. начиная с этой версии PHP вполне себе умеет потоки.
и miltu_curl и еще было третье решение, но забыл…

pthread вполне рабочая штука, не требующая особой магии, полностью согласен.

Затем, что проект размера Badoo, большая часть которого написана на пхп, двумя пальцами на жаве не перепишешь. Это слишком дорогой. Кроме того, зачем ломать то, что работает? Ещё и штат новый набирать? А со старыми что делать?

В статье речь не про Badoo. А про студию веб дизайна

Цитирую Badoo: Подход из статьи нам близок: при решении своих задач мы тоже чаще всего используем связку PHP и Go, получая преимущества от обоих языков и не отказываясь от одного в пользу другого.

Кроме дизайна мы ещё занимаемся серьезным энтерпрайзом, просто скрываемся :)

Java или Kotlin? Хорошая шутка, но нет.

Node.js убог и не безопасен.

Typescript вообще к чему?

Чувак, ты выбрал не те языки в жизни, успокойся и пойди переучись.
Д'Артаньян, перелогиньтесь
Можно подробнее разложить, чем отличается от PPM, кроме возможности дописывать обработчики на стороне go? В новых проектах сижу на ppm, присматриваюсь к roadrunner. Но пока рассматриваю чисто как менеджер процессов обе тулзы.

Быстрее (мы просто не смогли нагрузить PPM до упора из-за его модели работы), для HTTPS не нужен сервер сверху. В остальном все похоже.

К слову, тут на Хабре так часто лошили/лошат PHP и совершенно не обосновано (даже когда вышла 7я версия и по скорости и качеству язык стал ничем не хуже других скриптовых). А про JS обычно молчок. Странности.
Не ясно что вы имеете в виду. В JS описываемая в статье проблема в принципе отсутствует

JS это хайп и мода, хаять его — значит плыть против течения. К этому наше сообщество не склонно. С PHP ситуация аналогичная, но со знаком минус. К сожалению, люди забывают, что говнокод создается в конечном итоге ими же, а не инструментом, и говнокода на том же JS тонны. PHP сейчас хорошо развивается и будет сохранять свою нишу еще долгое время. Вспомните, был хайп ruby/ror, но где это щас все? Потихоньку и эта волна спадет, а PHP впитает в себя лучшее и будет спокойно жить дальше.

значит плыть против течения
Плыть против кармы ) наличие нормально обсудить не даёт.
Интересно, а можно что-то подобное сделать без go-прослойки, с помощью контейнеров в, скажем, Kubernetes? Как мне представляется — запускаем N контейнеров-воркеров, запросы же на них будут перекидываться load-балансером самого Kubernetes. Или я что-то упускаю?

Приветствуем создателя RoadRunner и соавтора оригинальной статьи — Lachezis

Мы знали, что сможем написать веб-сервер на чистом PHP (PHP-PM) или с использованием С-расширения (Swoole). И хотя у каждого способа есть свои достоинства, оба варианта нас не устраивали — хотелось чего-то большего.


Слабоватые аргументы. Чем не устраивали? Вы же наверно что-то смотрели, с чем-то сравнивали. Можно об этом подробнее?
Например, есть такое решение github.com/amphp/http-server, есть тот же PPM.
Что-то можете сказать об этом?

Завести Swoole в режиме "поставил и забыл", мы не смогли. Пробовали несколько раз в течении 4 последних лет.


Нагружая PPM мы упирались в сам балансировщик. Насчет AMP не скажу, еще не гоняли его.


Вообще изначально RR был нужен не для HTTP, а для AWS SWF воркеров. В итоге это не HTTP сервер, а app server (любой фронтенд). Например https://github.com/spiral/php-grpc

Если вы всеравно используете rpc, то чем не устраивало использование:
grpc & grpc-gateway?
Интересно будет опробовать
Но мне лично Go не взошел
Очень уж специфичным показался
Но это только на мой взгляд!

Если их действительно можно будет объединить во что-то общее… Думаю, должна получиться как минимум Ракета_)
Вдруг, кому-то пригодится для быстрого старта.

Очевидное отличие разработки под RoadRunner от классической схемы сохранил_скриптперезагрузи_страничку состоит в том, что теперь это выглядит сохранил_скриптперезагрузи_демонаперезагрузи_страничку. Руками это делать скучно, но умные люди уже всё придумали. Для автоматизации понадобится modd.

Мой modd.conf выглядит вот так:
**/*.php {
    daemon +sigterm: ~/bin/roadrunner/1.2.6/rr serve -d -v -c /home/foo/src/boo/rr.json
}

Кстати, вопрос к Lachezis: почему-то у меня не получилось использовать rr http:reset — php код сервера не обновляется. Команда rr http:reset -d -v -c /home/foo/src/boo/rr.json отрабатывает — в логе появляется сообщение new worker pool, но код не перегружается. Или это так и задумано и http:reset нужен для чего-то другого? Возможно, есть еще какой-то более правильный способ обновления кода на сервере, пожалуйста поделитесь.

и rr.json
{
  "http": {
    "address": "0.0.0.0:8880", <-- биндинг на ваше усмотрение
    "workers": {
      "command": "/usr/bin/php /home/foo/src/boo/main.php",
      "pool": {
        "numWorkers": 4 <-- число ядер CPU
      }
    }
  }
}

Заходим в /home/foo/src/boo и запускаем modd, теперь при изменении в php файлах, находящихся в директории /home/foo/src/boo, rr процесс автоматически перезагрузится и вы сможете увидеть сделанные изменения. Рекомендую использовать tmux, если дело происходит в терминале ssh

Еще можно maxJobs использоват для разработки. Должен перезагружаться (есть тесты), http:reset и задуман для безопасной перезагрузки пула. Если стабильно воспроизводится то кидайте нам багу на гитхаб (желательно со способом воспроизвести).

#76 Пока писал баг-репорт, выяснил, что код перегружается успешно, но совсем перестает работать error_log. Поскольку я использовал его для отладки, то у меня и сложилось впечатление, что после http:reset код не обновляется, а оказалось, что проблема есть, но она в чём-то другом
В 1.2.8 всё заработало!

Теперь возник вопрос, а насколько дорого перезапускать весь сервер целиком по сравнению с оперцией http:reset?

Поясню суть проблемы, если вызвать rr http:reset при выключенном сервере, то в ответ получаем Error: dial tcp 127.0.0.1:6001: connect: connection refused, что в принципе логично. Можно ли сделать так чтобы в этом случае rr всё-таки запускался? Иными словами, http:reset_or_start

При использовании modd c директивой daemon +sigterm достаточно запустить только modd и он автоматически запустит новый rr и будет за ним следить, при этом rr будет выводить свои сообщения в этом же самом окне. Если же использовать конструкцию в modd.conf вида prep: rr http:reset, то сначала в одном окне нужно запустить rr serve, а в другом окне modd, мелочь, конечно…

Или посоветуете использовать для разработки daemon +sigterm и «не придумывать себе», а http:reset оставить только для продакшена?

В проде перезагружать сервер будет очень дорого, может отвалится хелсчек и система начнет скейлится. Для локальной разработки можно использовать то что работает, но я все же рекомендую http:reset.


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

Всё-таки PHP даже 7.3 ужасен для быстрой разработки в Enterprise среде. По синтаксическим плюшкам он крайне сильно отстаёт от современных языков (тому же Kotlin или C#).
Хотя разработчики стараются, клепают версию за версией. Но догнать передовые языки, как мне кажется, уже не смогут.
Расскажите об этом ветеранам энрерпрайза, которые С с решёткой на дух не переносят. Будь я боссом, я бы на свой бекэнд такую молодую технологию, как C# не пустил… Вот ещё десяток другой лет пройдет, тогда и посмотрим… Плюшки, извините, никому в продакшене не нужны, 80% жизни программы приходится на её сопровождение, которое может длится десятилетиями. Чем проще всё написано, тем лучше.
Много ли энтерпрайза на PHP?)

P.S. Не троллинга ради, исключительно любопытство
Фейсбук — это много или мало? :D
P.S. Да, знаю, не всё там так просто, собстна даже HHVM из-за этого возник, но тем не менее ;)
UFO landed and left these words here
Спасибо за замечательный продукт, все завелось с пол пинка!
Скажите можно ли использовать путь к конфигу отличительный от .rr.json?
Only those users with full accounts are able to leave comments. Log in, please.