Comet — PHP-фреймворк для быстрых REST API

    Два года я писал микросервисы на Go, используя генераторы кода на базе Swagger. Получались довольно компактные и очень быстрые решения.

    Сейчас использую PHP, поэтому решил найти средства для решения тех же самых задач в экосистеме языка. Знаю и люблю Laravel и Symfony, но тащить их в проекты не хотелось — слишком много батареек, за которые приходится расплачиваться крутой кривой входа в проект и производительностью.
    PHP фреймворк для создания REST API

    В итоге появился Comet — современный фреймворк на базе PHP для разработки быстрых API, использующий наработки команд SlimPHP и Workerman. Десятки тысяч RPS на обычной виртуальной машине и латенси менее миллисекунды!

    Чтобы не быть голословным, приведу результаты тестирования Comet и других популярных фреймворков. Для более честного сравнения, все тяжелые модули вроде ORM были удалены из сборки всех участников теста.

    Первый кейс — имитация highload-нагрузки в виде тысяч конкурентных запросов wrk на метод, возвращающую в виде простого текста строчку приветствия «Hello, World!»:

    image

    Для оценки минимальной задержки использовался вариант, в котором единственный клиент последовательно отправлял по одному запросу на тот же самый ендпойнт:

    image

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

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

    use Comet\Comet;
    
    require_once __DIR__ . '/vendor/autoload.php';
    
    $app = new Comet();
    
    $app->get('/hello', function ($request, $response) {
        $response
            ->getBody()
            ->write("Hello, Comet!");      
        return $response;
    });
    
    $app->run();
    

    Все довольно прозрачно: использование роутера и замыканий обеспечивает компактный код, более похожий на то, к чему привыкли разработчики NodeJS / Express.

    Я выложил на GitHub весь код и планирую расширять возможности фреймворка:

    https://github.com/gotzmann/comet

    Буду рад комментариям, коммитам и, конечно — использованию Comet в реальных проектах :)
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 59

      +1
      А о чем нам вообще могут говорить синтетические тесты нагрузки, где тестируемый метод просто возвращает заранее заготовленную строку?
        0
        Об оверхеде, который накладывает фреймворк на любые решения. Это как планка, выше которой прыгнуть не получится :) Для каких-то проектов это не критично, но там, где по условиям техзадания вызов API должен гарантированно укладываться в 1мс, а оверхед фреймворка на обслуживание такого вызова выше, придется искать другие варианты.

        Но в целом согласен с вопросом и планирую дополнить раздел тестов более-менее реальными кейсами «сходить в базу» и «отрендерить HTML шаблон». Для себя тестиовал поведение Comet с PDO и Eloquent — последний оказался в два раза медленнее на простых вставках в базу.
          0
          Поясните непонятливому: если важна каждая милисекунда, то зачем вообще нужен оверхед в виде какого-либо фреймворка? Что проще — написать своё быстрое на «чистом» php или взять «универсальное решение» и бесконечно пытаться оптимизировать окружение для его ускорения внедряя всё новые и новые «ускорялки»? Есть пример хайлоад проекта на популярном фреймворке?
            +1
            Если важны миллисекунды, то php не берут.
            Если есть возможность ускорить проект практически бесплатно — то почему бы и нет?
              0
              Для facebook и vk не важны, видимо? Помимо технической прагматичности есть ещё экономическая составляющая, благодаря которой php очень даже берут.
              Что имеете ввиду под «бесплатно»? Перейти с одного фреймворка на другой — это не бесплатно.
                +1
                Именно потому фб и напилил HHVM, а ВК KPHP, что просто php уже не подходил. Банально начали делать на том, на чем могли, а переписать уже слишком дорого. На фоне затрат на смену стека, смена фреймворка — копейки.
              +1
              На «чистом» PHP будет медленнее в разы! Однозначно нужно использовать слой в виде workerman / swoole / reactphp и подобных библиотек. А с ними оверхед на код фреймворка становится совсем минимальным. Если вызов API выполняется менее чем за миллисекунду, дальнейшая «оптимизация» для многих проектов не имеет смысла.
                –1
                А workerman разве не на «чистом» php написан? ;)
            0
            Да что тут можно вообще говорить, когда используется для теста Laravel, а не Lumen…
              0
              Согласен, надо добавить в тесты Lumen — но по ожиданиям, его производительность будет где-то на уровне Symfony (судя по тестам TechEmpower Benchmarks)
            +1
            использующий наработки команд SlimPHP и Workerman

            По-моему, Вы используете не наработки этих команд, а их библиотеки


            Как Comet поведёт себя при множестве блокирующих операций?

              0
              Это зависит от настроек сервера Comet — количества доступных для работы воркеров. Длинные блокирующие операции выжирают пул доступных воркеров и заставляют новые запросы ждать своей очереди. Звучит не очень в теории, но на практике даже под нагрузкой Comet не проигрывает в производительности ни серверам на Go, ни проектам на NodeJS, если блокирующим фактором выступает, например, база данных.
              0
              Жаль нет в сравнении Yii2, а он вроде как по другим статьям побыстрее будет чем Symfony и Laravel в этом деле.
              Хотелось бы понять так ли это и на сколько.
              +4
              Я правильно понимаю, что ваш фреймворк по сути состоит из двух файлов?
                +1
                Если не считать composer.json — так и есть :) Пока не решил, какие части фреймворка стоит включить в базовой поставке. Думаю, расширенные версии Request / Response, миддлваре авторизации и средства миграции БД пригодятся всем пользователям.
                  0
                  Я такой же «фреймворк» писал для yii2 и workerman. Тоже в два файла :)
                  Тоже было огромное отличие в производительности, а потом заметил, что у меня op-cache не был установлен (оказалось что в centos это отдельный пакет). Установил op-cache и разница получилась всего в два раза. Возможно автор статьи тестировал с отключенным op-cache.
                    0
                    Насколько я знаю, opcache по умолчанию включен во всех дистрибутивах PHP7. Но проверил на всякий случай php.ini в докер-контейнере на базе Ubuntu 19.10 и PHP 7.4, в котором тестирую все фреймворки — как и ожидалось — opcache.enable=1
                      0
                      Да в убунте он включен по дефолту, это в центосе только почему-то решили сделать иначе.
                      В принципе по графикам с латенси видно, что отличие всего в два раза.
                      А на графиках rps такое большое отличие потому что в php-fpm было мало воркеров.
                      Укажите пожалуйста сколько воркеров было использовано в тесте у comet и сколько у php-fpm.
                      В принципе даже на TechEmpower Benchmarks
                      видно, что workerman быстрее чистого php не более чем в 2 раза, ну и в 5 раз по сравнению с slim.
                      А у вас workerman+slim получился в 7 раз быстрее чем просто slim. Т.е. во время тестов было указано недостаточное количество воркеров и php-fpm просто захлёбывался, оказавшись в неравных условиях.
                      К слову, в TechEmpower Benchmarks чистый пхп добился таких высоких результатов, потому что они создают около 1000 воркеров. Из-за чего конечно сильно повышается расход оперативной памяти, но многие крупные проекты так и делают. Просто увеличивают количество воркеров. Память дешевле, чем переписывать весь код.
                      Я использую workerman уже очень давно и он хорошо себя показывает на микросервисах без блокирующих операций, но что-то более менее серьёзное, что ходит в базу и т.д. уже не даёт почти никаких преимуществ, но сильно усложняет разработку и деплой, поэтому в проде я его использую только там, где мне максимум приходится использовать redis.
                        0
                        Спасибо за инсайты, настройки Nginx/FPM брал как раз на Techempower, но на результаты мог повлиять факт тестов из докер-контейнеров на платформе Windows. Надо будет прогнать их на более приближенном к реалиям окружении Linux.

                        Кусочек конфига, определяющий количество воркеров:

                        worker_processes  auto;
                        worker_rlimit_nofile 200000;
                        events {
                            worker_connections 16384;
                            multi_accept off;	 
                        }
                        
                          0
                          Кусочек конфига, определяющий количество воркеров
                          Это воркеры nginx, а я про воркеры php-fpm:
                          pm.max_children = 1024
                          В вашем фреймворке:
                          $worker->count = (int) shell_exec('nproc') * 4;
                          т.е. в 4 раза больше чем ядер, поэтому мне было интересно сколько у вас было воркеров для пхп.
                          Кстати, у них в тесте сейчас количество воркеров уменьшается с 1012 до 512, если ядер два:
                          if [ $(nproc) = 2 ]; then sed -i «s|pm.max_children = 1024|pm.max_children = 512|g» /etc/php/7.4/fpm/php-fpm.conf; fi;
                            0
                            Подрихтовал все конфиги и провел тесты на Linux-машине Hetzner:

                            github.com/gotzmann/benchmarks

                            Результаты отрыва получились еще более впечатляющие :) Интересно понять, связано ли это на самом деле с конфигурированием, или все-таки workerman позволяет ускоряться до нереальных показателей.
                  0
                  Хм. есть же Falcon
                    0
                    Надо посмотреть поближе, рекомендовали Phalcon Micro. Несколько лет назад у меня с ним были проблемы (то ли не мог собрать пакет, то ли он вообще не поддерживал разработку на Windows-хостах), поэтому в этот раз я как-то прошел совсем мимо.
                      0
                      Сейчас он доступен на хостингах c cPanel через EasyApache, через pear, в репозиториях популярных дистрибутивов, поддерживает PHP вплоть до последнего 7.4. Реально быстрая штука с простым синтаксисом.
                    +3
                    Я думаю, что предзагрузка должна очень сильно оптимизировать существующие фреймворки. Да и RoadRunner сейчас это очень неплохо делает.
                      0
                      Предзагрузка помогает, но не настолько существенно, как хотелось бы. Badoo на Хабре приводит следующие цифры: «переход c PHP 7.2 на PHP 7.4 даёт +10% к производительности на нашем endpoint’е, а preload даёт ещё 10% сверху».

                      В следующих версиях Comet планирую тоже поиграться с предзагрузкой и добавить более быстрые имплементации PSR-7 компонентов.
                        0

                        Тоже странно, почему нет RR в сравнении. Используем его в проде уже более полу-года (все php-приложения перевели на работу с ним, забыв про nginx+fpm) — полёт нормальный. Кстати, он довольно легко интегрируется с тем же Laravel, для чего написал и поддерживаю пакет avto-dev/roadrunner-laravel. По нашим тестам буст производительности (очень многое зависит от самих приложений) составил от 20 до 80 процентов.

                          +2

                          Ну Spiral работает на RR раз в 5-7 быстрее Symfony, и это на точках с ORM (согласно тому же бенчмарку). Сравнивать микро сборки с фулл стеком достаточно ненадежное занятие.

                            0
                            В тестах использовал обрезанный Symfony без Doctrine и прочих бандлов — только роутинг и контроллеры.
                              +1

                              Привет Антон :) Именно поэтому и было интересно посмотреть на бенчи указанного в посте решения под соусом RR. Кстати, дополню про "20 до 80 процентов" — это именно на "тяжелых" endpoints, условно-статичные (ping, static view) просто уходят в космос по сравнению с традиционным nginx+fpm.
                              В сторону Spiral пока лишь присматриваюсь, но и этот проект кажется очень интересным, обязательно что-нибудь на его основе сделаем.

                                0
                                А есть у Spiral какие-то микросборки для тестирования? Не смог собрать для него урезанную версию без ORM и прочих плюшек, а хотелось бы для сравнения github.com/gotzmann/benchmarks
                              +1
                              Приложение на Comet я запускаю прямо из командной строки Windows/ Linux и для его работы не требуется ни RR, ни Nginx или FPM — считаю, это огромное преимущество. Зачем RR, если нативное приложение на PHP работает быстрее без обвязки?
                                +1

                                RR это тоже нативное приложение которое можно запускать из командной строки. Чем больше кода будет в вашем приложение, тем сильнее вы начнете замечать провисания на инициализации.

                            +3
                            gotz, Классный троллинг ;) Назвать демку к slim фрейворком.
                              0
                              Спасибо :) Это не демка Slim, просто Comet «магически» отправляет все не определенные в классе вызовы внутреннему инстансу приложения Slim — поэтому код базовых примеров выглядит один в один.

                              Как сказано в описании проекта на GitHub, Comet — это гибрид Slim и Workerman, приправленный собственной магией, которой будет больше в следующих релизах :)
                              0
                              Два года я писал микросервисы на Go
                              … Однако, как всё хорошо начиналось :). Что подвергло REST-ы писать на РНР?
                                0
                                Для Go был мощный корпоративный фреймворк для генерации 90% бойлерплейта по сваггер-спеке. Аналогичного по возможностям в опен-сорс не нашлось, а писать вручную типичный код на го для валидации и перекладки данных между JSON и базой данных — такое себе удовольствие. PHP более краткий и мощный. Для Python есть Connexion от Zalando — но увы, это не мой язык :)
                                +2
                                «Не упоминай имя REST всуе.»

                                Не увидел в вашем примере ничего такого, что позволяло бы проще писать приложения с архитектурой REST. Хотя правильнее сказать — ничего такого, что заставляло бы писать этом стиле. Т.е. накладывало бы какие-то ограничения на программиста, которые исходят из ограничений описанных в REST.
                                У вас просто минималистичный web-фреймворк, который ни к чему не обязывает.
                                  0
                                  Остальные части фреймворка пока не готовы к публичному показу и концептуально не проработаны. Замечание справедливое :)
                                  +6
                                  Замечательный фреймворк для вывода фразы «Hello world» на экран. Если я когда-нибудь захочу написать приложение, выводящее фразу «Hello world» — обязательно им воспользуюсь! (на самом деле нет)
                                    0
                                    Ну для реальной работы у меня в приложении помимо Comet подключен Eloquent, Monolog и миграция базы на Phinx. Тащить все это в сам фреймворк не вижу смысла :) У других разработчиков могут быть собственные предпочтения по выбору библиотек.
                                      +1

                                      Мне кажется речь не совсем о том, что присутствует или не присутствует в вашем фреймворке(все понимают, через composer можно прикрутить что угодно), а речь о том, что тесты надо было демонстрировать хотя бы с загрузкой сервис-провайдеров/бандлов, с чтением конфигурации, заполнением контейнера зависимостей или сервис-локатора минимально необходимыми компонентами — сделать тот же "hello world", но включая минимальный набор того, что действительно понадобится во время разработки. Я почему-то уверен, что вы взяли тот же laravel и не выпиливали из него сервис-провайдеры, которые не будут использоваться(бродкасты, авторизация, вьюхи, сессии, очереди, dbal и.т.п), не отключали искоробочные глобальные middleware и прочее. Я не говорю, что оно сразу в одном вырастет, а в другом резко просядет, однако результаты будут немного справедливее, чем наблюдается сейчас… Отсюда и такие поспешные выводы про фрэймворк для "hello world".

                                        +1
                                        Я старался провести максимально честное сравнение и выпилил из Laravel / Symfony все бандлы, которые вообще можно было выпилить. Буду рад, если посмотрите и подскажете, как их еще можно ускорить, я выложил бенчмарки в репозиторий:

                                        github.com/gotzmann/benchmarks

                                        Провел сравнительные тесты на машине Hetzner и разница между Comet и остальными фреймворками получилась еще более внушительной.
                                          0

                                          Не усмотрел, что вы уже давали ссылку в комментариях. Laravel действительно раздет до косточек, всё стало нагляднее и вопросы отпали, спасибо!

                                    0
                                    я не понимаю, зачем все эти реакты, воркманы и т.д. если есть swoole с корутинами.
                                      +2
                                      Для того, чтобы не ломать привычный поток разработки на PHP :) Не нужно изучать новые подходы и разруливать целый класс новых проблем и нюансов асинхронного кода — ведь все летает и без этого. Но если хочется бОльшего именно в рамках PHP — да, Swoole интересный вариант. Но я программировал в такой парадигме на Go / Node.js и мне не очень понравилось.
                                      0
                                      Так что там не так с отваливающимся контроллером в примере?
                                        0

                                        gotz прошу прощения, но какой смысл сравнивать проекты, которые работают по разным принципам? Если Вы воспользовались workerman в новом "фреймворке" получается, что это детище тоже работает асинхронно. Вы показали бэнчмарки сравнивая синхронные фреймворки с асинхронными. Может для справедливости к остальным фреймворка прикрутить Swool или что-то подобное и потом уже меряться, что скажете?

                                          0
                                          Так Comet — это и есть попытка использовать Slim в гиперускоренном варианте. Ну и дополнительные плюшки, заточенные именно под REST. Сейчас добавляю HTTP-клиент, сравниваю тот же привычный Guzzle / новый легковесный Buzz и собственную реализацию поверх стримов PHP (file_get_contents).
                                          0
                                          Не хотите ломать, не используйте корутины. В чём workman лучше? сообщество меньше, библиотек меньше, использование в больших проектах — меньше. В чем смысл?
                                            0
                                            Swoole я тоже планирую попробовать в качестве «ускорителя» в будущем, но Workerman прямо сейчас лучше тем, что поддерживает разработку на Windows-хостах. При этом не требуется ничего компилировать или подключать сторонние расширения — все работает просто из коробки. Для меня это было важно, по умолчанию сейчас корпоративный ноутбук Dell / Windows 10 :)
                                              +1
                                              windows, тогда понятно. Обязательно попробуйте, приятно удивитесь. Кстати, корутины на swolle, даже немного быстрее чем на Го.
                                              Но windows для разработки, это конечно печаль.
                                            +1
                                            Зашёл на github посмотрел пример контроллера в описании и…
                                            он всегда будет возвращать 500 ошибку…
                                            public function setCounter(Request $request, Response $response, $args)    
                                                {        
                                                    $body = (string) $request->getBody();
                                                    $json = json_decode($json);
                                                    if (!$json) {
                                                        return $response->withStatus(500);
                                                    }  
                                                    self::$counter = $json->counter;
                                                    return $response;        
                                                }

                                            Когда такие косяки прямо в примерах в описании, сразу теряется доверие к автору, и пропадает желание использовать.
                                              0
                                              он всегда будет возвращать 500 ошибку…

                                              Я конечно не xDebug, но как вы пришли к такому выводу?
                                                +1
                                                $body = (string) $request->getBody();
                                                $json = json_decode($json);


                                                надо всё-таки
                                                $json = json_decode($body);
                                                  0
                                                  Да, дошло. Спасибо
                                                0
                                                Пример с контроллерами был оформлен по ходу подготовки документации прямо из головы, спасибо за наводку :) Исправил ошибки в примере, проверил на локалхосте работу и обновил README на GitHub.

                                              Only users with full accounts can post comments. Log in, please.