Spiral: высокопроизводительный PHP/Go фреймворк



    Привет, Хабр. Меня зовут Антон Титов, CTO компании Spiral Scout. Сегодня я хотел бы рассказать вам про нашего PHP-слона. А точнее про вторую версию опен-сорсного full-stack PHP/Go фреймворка — Spiral.

    Spiral — это компонентный full-stack фреймворк, разрабатываемый нашей компанией более одиннадцати лет и обслуживающий под сотню реальных проектов. Программный пакет основан на множестве открытых и собственных библиотек, включая RoadRunner и Cycle ORM.

    Фреймворк совместим с большинством PSR рекомендаций, поддерживает MVC и работает в 5-10 раз быстрее Laravel/Symfony.

    Если вы никогда не слышали о Spiral и гадаете, что такое PHP/Go фреймворк и куда делась первая версия — добро пожаловать под кат.

    О Фреймворке


    Разработка Spiral была начата в 2008/09 годах в виде переносимого ядра для приложений, разрабатываемых под фриланс. В 2010 годы мы окончательно сформировали аутсорс компанию и с тех пор занимались улучшением нашего стека.

    В итоге, фреймворк преобразовывался в набор независимых компонентов, объединенных общим интеграционным слоем.

    Единственный публичный анонс первой версии произошел на Reddit в 2017 году и дал нам понять, что технически Spiral не особо выигрывала у конкурентов на тот момент. Мы учли полученный фидбек, опыт более сложных проектов и закончили вторую версию в мае 2019 года.

    Спустя год документирования и обкатки на реальных проектах мы готовы представить данную разработку на суд общественности.

    Основным отличием Spiral 2.0 от предыдущего поколения фреймворка и, пожалуй, от всех остальных существующих PHP-фреймворков является интегрированный сервер приложений RoadRunner, а также адаптация архитектуры под долгоживущую модель выполнения (режим демона).

    Гибридный Рантайм


    Основной концепцией фреймворка является симбиоз между сервером приложений, написанным на Golang, и PHP-ядром. Код PHP приложения загружается в память только один раз — так вы получаете значительную экономию ресурсов, но теряете возможность запускать WordPress.



    Больше о гибридной модели можно прочитать тут и тут.

    Сервер отвечает за всю инфраструктурную часть: HTTP/FastCGI, общение с брокерами очередей, GRPC, WebSockets, Pub/Sub, кэш, метрики и т.д.

    Написание кода под Spiral практически не отличается от кода под любой другой фреймворк. Однако к принципам SOLID придется относиться более ответственно. Если раньше кэширование данных пользователя в синглтоне вызывало лишь сильную боль на код-ревью, то сейчас такой код просто не будет работать корректно.

    Знания Golang не являются обязательными для работы с платформой. Однако, выучив второй язык, можно создавать практически бесшовные интеграции c Golang библиотекам. Например встроить движок полнотекстового поиска или написать свой драйвер Kafka.

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

    Фреймворк предоставляет набор инструмент, таких как IoC замыкания, middleware, интерцепторы доменного слоя и immutable-сервисы.

    $container->runScope(
        [UserContext::class => $user],
        function () use ($container) {
            dump($container->get(UserContext::class);
        }
    );

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

    Производительность


    В классе full-stack PHP фреймворков конкуренцию Spiral в основном составляют сборки на основе Swoole и несколько микро-фреймворков.



    Полные бенчмарки доступны тут и тут.

    Для нас производительность является побочным эффектом выбранной архитектуры. Мы уверены, что при должной конфигурации и заменe PSR-7 на более легкую абстракцию, производительность можно поднять на 50-80% (это доказывает пример ubiquity-roadrunner).

    О Swoole. Swoole имеет меньший оверхед чем RoadRunner, так как работает с PHP в рамках одного процесса и написан на С++. В отдельных задачах можно выжать намного больше, чем используя сервер на Go. Плюс у вас появляются корутины, что трудно игнорировать.

    С другой стороны RoadRunner менее инвазивен (внешние зависимости не требуются), есть больше готовых инструментов, он проще расширяется и работает под Windows.

    Код работающий под Spiral будет прекрасно работать на Swoole, так что всегда можно переехать!

    PSR-* и Компоненты


    Большинство компонентов Spiral не являются обязательными для вашей сборки, разница между micro- и full- сборкой заключается только в содержимом composer.json. При необходимости можно воспользоваться интерфейсами для замены стандартных библиотек на альтернативные реализации.

    HTTP слой фреймворка написан с учетом стандартов PSR-7/15/17, можно смело менять роутер, реализацию сообщений и т.д.

    Большинство библиотек фреймворка можно использовать вне фреймворка. Так, например, RoadRunner прекрасно работает с Symfony и Laravel, а Cycle ORM будет доступна в Yii3.

    Компоненты сервера


    Помимо PHP-компонентов, сборка RoadRunner включает несколько библиотек, написанных на Golang. Большинством сервисов сервера можно управлять из PHP.

    В частности, есть компонент очередей, поддерживающий работу с брокерами AMQP, Amazon SQS и Beanstalk. Библиотека умеет корректно останавливаться, переподключаться и распределять любое количество входящих очередей на несколько воркеров.

    Из коробки идет мониторинг на Prometheus и health-check точки, горячая перезагрузка и ограничение по использованию памяти. Для распределенных проектов есть GRPC сервер и клиент.

    INFO[0154] 10.42.5.55:51990 Ok {2.28ms} /images.Service/GetFiles
    INFO[0155] 10.42.3.95:50926 Ok {11.3ms} /images.Service/GetFiles
    INFO[0156] 10.42.5.55:52068 Ok {3.60ms} /images.Service/GetFiles
    INFO[0158] 10.42.5.55:52612 Ok {2.30ms} /images.Service/GetFiles
    INFO[0166] 10.42.5.55:52892 Ok {2.23ms} /images.Service/GetFiles
    INFO[0167] 10.42.3.95:49938 Ok {2.37ms} /images.Service/GetFiles
    INFO[0169] 10.42.5.55:52988 Ok {2.22ms} /images.Service/GetFiles


    Есть вебсокеты, их можно авторизовать из PHP приложения и подключать к pub-sub шине (в памяти или на Redis). На текущий момент производится обкатка Key-Value драйверов.

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


    Фреймворк не требует наличия PHP-FPM и NGINX. А все Golang-компоненты имеют драйверы для работы без внешних зависимостей. Таким образом, вы можете использовать очереди, websockets, метрики, не устанавливая внешние брокеры или программы.

    ./spiral serve -v -d

    Spiral не важно, пишите ли вы огромное распределенное приложение или небольшой сайт, посылающий «напишите нам» в фоне. В любом случае вы можете использовать одинаковые инструменты, унифицируя поведение локального и продакшн окружений.

    Так как HTTP-слой не является обязательным, можно писать консольные приложения, обрабатывающие данные в фоне, используя пакет очередей. Мы используем такие программы для миграций данных.

    Cycle ORM


    В качестве ORM из коробки идет Cycle ORM. Это Data Mapper движок очень похожий на Doctrine функционально, но сильно отличающийся архитектурно.

    Как и Doctrine, Cycle может работать с чистыми доменными моделями, самостоятельно генерируя миграции и расставляя внешние ключи. Схему маппинга можно описывать кодом либо собирать из аннотаций. А вот вместо DQL используются классические Query Builders.

    // загрузить всех активных пользователей
    // и выбрать все оплаченные заказы отсортированные по дате
    $users = $orm->getRepository(User::class)
        ->select()
        ->where('active', true)
        ->load('orders', [
            'method' => Select::SINGLE_QUERY, // force LEFT JOIN
            'load'   => function($query) {
                $query->where('paid', true)->orderBy('timeCreated', 'DESC');
            }
        ])
        ->fetchAll();
    
    $transaction = new Transaction($orm);
    
    foreach($users as $user) {
        $transaction->persist($user);
    }
    
    $transaction->run();

    Cycle работает быстрее Doctrine на выборках, но медленнее на persist. Движок поддерживает сложные запросы с несколькими стратегиями загрузки, прокси классы, embeddings и предоставляет переносимые транзакции вместо глобального EntityManager.

    Больше деталей можно будет услышать на PHP Russia 2020.

    Основная “фишка” ORM — возможность менять маппинг данных и связей в рантайм. Говоря простыми словами, вы можете позволить пользователям самостоятельно определять схему данных (DBAL поддерживает интроспекцию и декларирования схем баз данных).

    Больше о сравнение Cycle, Eloquent и Doctrine 2 можно прочитать тут.

    Быстрое прототипирование


    Spiral включает несколько инструментов для ускорения и упрощения разработки. Основными являются авто-инъекция зависимостей, авто-конфигурация и автоматический поиск моделей, используя статический анализ. Консольные команды позволяют генерировать большинство необходимых классов и легко кастомизируются.

    Для интеграции в IDE есть система быстрого прототипирования. Используя магический PrototypeTrait можно получить быстрый доступ к подсказкам в IDE.



    Трейт автоматом находит репозитории ORM, стандартные компоненты и умеет индексировать ваши сервисы по аннотациям. Под капотом используется магический метод __get, что гарантирует вам быстрое прохождение код ревью.

    Достаточно запустить команду `php app.php prototype:inject -r`, и система прототипирования автоматически удалит всю магию:

    namespace App\Controller;
    
    use App\Database\Repository\UserRepository;
    use Spiral\Views\ViewsInterface;
    
    class HomeController
    {
        /** @var ViewsInterface */
        private $views;
    
        /** @var UserRepository */
        private $users;
    
        /**
         * @param ViewsInterface $views
         * @param UserRepository $users
         */
        public function __construct(ViewsInterface $views, UserRepository $users)
        {
            $this->users = $users;
            $this->views = $views;
        }
    
        public function index()
        {
            return $this->views->render('profile', [
                'user' => $this->users->findByName('Antony')
            ]);
        }
    }

    Под капотом используется PHP-Parser.

    Безопасность


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

    Вам будут доступны компоненты для валидации сложных запросов (Request Filters), CSRF и шифрования (на основе defuse/php-encryption). Работа с cookies и session поддерживает aнти-тамперинг и подписывание данных на стороне сервера.

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

    Либо все типы токенов одновременно, если к вам внезапно прилетело требование “срочно подключите SAML/SSO/2FA!” :(

    Авторизация доступа выполняется через RBAC компонент с некоторыми доработками, позволяющими работу в режиме DAC и ABAC. Есть поддержка множества ролей, аннотаций для защиты методов контроллеров и система правил.

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

    Шаблонизатор


    Если вам нравится только Twig — можете пролистать данный раздел, просто установите расширение и пользуйтесь знакомыми инструментами. :)

    Из коробки идет шаблонизатор Stemper, а точнее, библиотека для создания собственных DSL разметок. В частности присутствует полноценный лексер, несколько грамматик, парсер и доступ к AST (по аналогии с PHP-Parser Никиты).

    Есть возможность парсинга нескольких вложенных грамматик. Так, например, вы можете использовать директивы Laravel Blade и собственный DSL (в виде HTML тегов) разметки внутри одного шаблона. Получается что-то вроде web-components на стороне сервера.

    Мы используем этот компонент для описания сложных интерфейсов, используя простые примитивы и правила.

    <extends:admin.layout.tabs title="User Information"/>
    <use:bundle path="admin/bundle"/>
    
    <ui:tab id="info" title="Information">
      User, {{ $user->name }}
    </ui:tab>
    
    <ui:tab id="data" title="User Settings">
      <grid:table for={{ $user->settings }}>
        <grid:cell title="Key">{{ $key }}</grid:cell>
        <grid:cell title="Value">{{ $value }}</grid:cell>
      </grid:table>
    </ui:tab>

    Поддерживается авто-экранирование с поддержкой контекста (например вывод PHP внутри JS блока автоматически преобразует данные в JSON), source-maps для работы с ошибками. Шаблоны компилируются в оптимизированный PHP код и после отдаются напрямую из памяти приложения.
    Stemper может полноценно работать с DOM документа (хоть и медленнее, если использовать специализированные инструменты).

    Развитие Фреймворка


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

    Не будем также отрицать, что некоторые вещи потребуют допиливания напильником. А какие-то вопросы могут звучать для нас впервые. Документация хоть и старается покрыть максимум компонентов, все же может провисать в отдельных местах.

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

    Много работы потребуется для улучшения и ускорения Cycle, а также переписывания Request Filters согласно последним RFC. В процессе разработки находится компонент для работы и эмуляции Key-Value баз данных.

    Многие вещи мы просто не успели перевести с первой версии. В планах восстановить пакеты ODM, панель администрирования, написать хороший профилировщик и т.д.

    Комьюнити и Ссылки


    Вы можете зайти в наше небольшое комьюнити на Discord. Telegram-канал.


    Весь код распространяется по MIT лицензии и не имеет каких-либо ограничений для коммерческого использования.

    Спасибо за внимание. Я надеюсь, наши инструменты пригодятся вам в проектах!
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 60

      +1

      После прочтения статьи остались вопросы. Скорость на синтетических тестах — это, конечно, хорошо, но как обстоят дела с памятью и отказоустойчивостью? Вот, например, разработчики Phoenix говорят, что не имеют проблем при работе с огромными (порядка миллиона, если мне не изменяет память) количествами коннектов по вебсокету к одной ноде. С другой стороны у go могут начаться проблемы из-за сборки мусора.
      Я так и не понял, в чём преимущество вашего фреймворка перед Symfony, Laravel или Phalcon? Symfony мне нравилась тем, что в ней всё работает, как в доках, пусть даже они и не всегда хорошие, а код на ней получается очень чистым, без лишнего хлама. Laravel простая, как пять копеек, даже не смотря на отвратительную производительность, полный треш в пакетах и чудачества eloquent, у неё всё равно есть своя область применения. У вас я открыл быстрый старт в документации, а там мне предлагают писать обработку аннотаций роутера или пользоваться каким-то странным классом с шаблонным кодом. Я что-то не так понял или и правда нужно нажимать так много лишних кнопок? Ну, да, пёс с ним, пользователи Лары как-то живут с её роутингом и ничего. Вы пишете, что в основном в фреймворк используется в b2b, но я так и не смог придумать ни одной причины, почему там нужно использовать интеграцию с го и не хватит просто php или питона, например. На каких задачах он сможет полноценно раскрыть свой потенциал?

        +2
        почему там нужно использовать интеграцию с го и не хватит просто php или питона, например.

        Если вопрос в принципе про использование сервера приложений roadrunner, он убирает оверхед на создание инстанса приложения на каждый запрос, что даёт хороший буст производительности, и легко добавляется даже в уже существующие приложения, в т.ч. на Symfony/Laravel.
          0
          Не понятно, за счет чего возникает «хороший» прирост производительности. Особенно если добавить это в существующее приложение на, допустим, laravel.
            +3
            Не понятно, за счет чего возникает «хороший» прирост производительности. Особенно если добавить это в существующее приложение на, допустим, laravel.

            За счёт того что не нужно инициализировать всё приложение, зависимости, сервисы и т.п. на каждый запрос, работа идёт в скрипте-демоне.
            Конечно, неэффективный код приложения, если таковой есть, не особо ускорится, ну и нужно будет учитывать при разработке, что процесс после запроса не завершится и не почистит всё за собой.
              0
              что процесс после запроса не завершится и не почистит всё за собой

              Т.е. придется доработать приложение что бы оно за собой еще и подчищало данные? А потом всегда в голове это держать и не забыть зачищаться? Если да, то вижу я в этом усложнение процесса разработки и море багов связанных с этим.
                +4

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


                Задача фреймворка как раз упростить разработку таких приложений и избавить вас от большинства багов.

                  –1
                  Не в любом другом. К примеру в erlang иммутабелен.
                  0
                  Т.е. придется доработать приложение что бы оно за собой еще и подчищало данные? А потом всегда в голове это держать и не забыть зачищаться? Если да, то вижу я в этом усложнение процесса разработки и море багов связанных с этим.

                  C#, Java, Scala, NodeJS, да практически всё что угодно ― на них сервера пишут именно так.

            0

            Спасибо за большой комментарий.


            Скорость на синтетических тестах — это, конечно, хорошо, но как обстоят дела с памятью и отказоустойчивостью?

            Все хорошо, мы написали rr как раз для того чтобы приложения выдерживали резкие нагрузки. Память зависит от количества воркеров и объема вашего приложения. Про поведение сервера под нагрузками можно почитать тут — https://habr.com/ru/post/431818/ (на ядре Symfony).


            Я так и не понял, в чём преимущество вашего фреймворка перед Symfony, Laravel или Phalcon? Symfony мне нравилась тем, что в ней всё работает, как в доках, пусть даже они и не всегда хорошие, а код на ней получается очень чистым, без лишнего хлама. Laravel простая, как пять копеек, даже не смотря на отвратительную производительность, полный треш в пакетах и чудачества eloquent, у неё всё равно есть своя область применения.

            Мы больше склоняемся к модели Symfony, но используем работу как со стеком (middleware) Laravel. Код не сильно отличается от этих фреймворков, и, пожалуй, не должен отличаться. Плюсом можно назвать то что для запуска даже сложного приложения вам не потребуется ставить зоопарк инструментов.


            У вас я открыл быстрый старт в документации, а там мне предлагают писать обработку аннотаций роутера или пользоваться каким-то странным классом с шаблонным кодом. Я что-то не так понял или и правда нужно нажимать так много лишних кнопок?

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


            Вы пишете, что в основном в фреймворк используется в b2b, но я так и не смог придумать ни одной причины, почему там нужно использовать интеграцию с го и не хватит просто php или питона, например. На каких задачах он сможет полноценно раскрыть свой потенциал?

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

              +1
              По поводу роутинга, у вас есть уникальная возможность перенести его в golang часть (только для Spiral), передавая в аттрибутах PSR-7 выбранный роут и параметры.
                0

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

            –2

            Мне непонятно, почему нет тестов чистых golang фреймворков? Я просто сильно сомневаюсь, что скрестив гепарда с улиткой вы выиграете в производительности у гепарда.

              +4
              Мне непонятно, почему нет тестов чистых golang фреймворков? Я просто сильно сомневаюсь, что скрестив гепарда с улиткой вы выиграете в производительности у гепарда.

              Вряд ли у ребят есть цель обогнать golang по производительности
                +2

                Привет, это именно PHP фреймфорк, так что с точки зрения PHP разработчика ничего не меняется. Golang компилируется в бинарный файл и не беспокоит вас своим присутствием.

                  –1
                  так что с точки зрения PHP разработчика ничего не меняется

                  Как не меняется, если «после каждого запроса приложение должно зачищать за собой»?
                    +1

                    Это делает сам фреймворк за ваc.

                      0
                      Я тогда видимо не понял фразу "ну и нужно будет учитывать при разработке, что процесс после запроса не завершится и не почистит всё за собой.". В существующем приложении придется что-то менять или нет?
                        +1

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

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

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

                          Но я бы сказал что писать приложения которые не готовы жить в формате демона и полагаются на перезапуск на каждый запрос — не очень хороший тон в принципе.
                            +1
                            Roadrunner (особенно если поменять стартовый рекомендуемый бесконечный цикл воркера) прекрасно живет и в режиме «иногда умираем» и в режиме «нормально отрабатываем PSR-7» и даже «иногда инклудим файлы с ошибками синтаксиса, но всё равно сами встаём»
                            0

                            Подскажите, пожалуйста, что значит "архитектура для работы в режиме демона"? Может быть пример на словах, как вы архитектурно что то разрулили. Просто мне кажется дело в коде больше, чем в архитектуре. Либо я что то не понял..


                            Ps.Пробовали road runner, получили неплохой буст! Проект на симфони, код был готов к долгоживущим работам

                              0
                              Подскажите, пожалуйста, что значит «архитектура для работы в режиме демона»? Может быть пример на словах, как вы архитектурно что то разрулили. Просто мне кажется дело в коде больше, чем в архитектуре. Либо я что то не понял..


                              Типично:

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

                              В режиме демона — нет старта частого. Нужно аккуратнее относится к ресурсам занимаемым.
                                0

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

                                  0
                                  А можно немного про стек вместо событий? Почему пришли именно к этому варианту. Какие, и есть ли, подводные камни? Интересный же наверное кейс.
                                    0

                                    Про это мы расскажем на конференции. :)

                                      0
                                      Реквестую так же статью. Думаю многим будет интересно.
                                        0

                                        Если кратко: стек может гарантированно заменять события, но не наоборот. При событиях обработка данных происходит вне контекста запроса, и требует множество костылей (before, after, stop propagation, priorities). Отдельно стоит отметить обработку ошибок, какая именно цепочка событий отработает предугадать сложно.


                                        Если в приложении такое дебажить еще как-то можно, то в долгоживущем 60% проблем на ранних стадиях находилось в событиях и хуках.


                                        Стеки же гарантированно ограничивают контекст приложения в момент запроса, если запрос заканчивается (ошибкой или нет) то вы гарантированно выйдете из стека и востановите состояние.


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

                      +6
                      Мне непонятно, почему нет тестов чистых golang фреймворков? Я просто сильно сомневаюсь, что скрестив гепарда с улиткой вы выиграете в производительности у гепарда.


                      Дело не в этом.
                      А то давно бы весь мир писал только на Java или Go, а PHP и Python умерли бы.
                      Есть другие соображения, кроме производительности исполнения кода.

                      Скажем, как легко найти исполнителей на PHP и сколько они стоят.
                      И как легко и сколько — если Go.

                      Недостаток производительности сервера легко компенсируется небольшими деньгами.
                      А вот недостаток программистов — компенсируется только огромными деньгами.

                      Или большая скорость прототипирования на динамических языках против большей надежности на статических. А скорость для современного бизнеса — это крайне важно.
                        +1
                        Скажем, как легко найти исполнителей на PHP и сколько они стоят.
                        И как легко и сколько — если Go.

                        Дык апологеты Go пишут что почти любого программиста можно за пару дней превратить в Go-программиста, в виду того, что язык спроектирован так, что его легко учить и можно чуть-ли ни с места в бой. Врут?


                        В моей предыдущей конторе несколько человек быстро перешло на Go и остались довольны. Как раз PHP разработчики.

                          +2
                          Скажем, как легко найти исполнителей на PHP и сколько они стоят.
                          И как легко и сколько — если Go.


                          Дык апологеты Go пишут что почти любого программиста можно за пару дней превратить в Go-программиста, в виду того, что язык спроектирован так, что его легко учить и можно чуть-ли ни с места в бой. Врут?

                          В моей предыдущей конторе несколько человек быстро перешло на Go и остались довольны. Как раз PHP разработчики.


                          Изучить язык легко.
                          Практически любой.

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

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

                          Я «переключал стек» у новичков, что приходили к нам, занимало это 2 недели моего личного времени. Это было выгодно и перспективно и окупилось в течении 2-х лет, так как предполагалось (и предположения оправдались), что новички будут работать у нас минимум 2 года.

                          Но вот например сейчас мне уже через 1,5 месяца нужно выдать готовый продукт. Который сам по себе имеет хитрые ньюансики внутри, которые я еще не знаю как мы реализуем. А тут еще и новый стек? И в этих услових обучать новичков, а потом наблюдать за рушащимся сроками deadline — совсем не хочется. В этих условиях я возьму уже того, кто умеет.

                      0
                      Антон, интересно ваше мнение о модуле php для Nginx (аналогичен по архитектуре и возможностям популярной связке LuaJit/Nginx в OpenResty):

                      github.com/rryqszq4/ngx_php7

                      По бенчмаркам TechEmpower легко обходит Swoole, Workerman, Load Runner и даже (неожиданно) LuaJit и фреймворки на Go.
                        0

                        Привет, трудно судить по бенчмарку учитывая что там чистый PHP — https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/PHP/php-ngx/app.php


                        Достаточно большой оверхед создают ORM и другие части фреймворков, так что такое сравнение не совсем честное.

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

                            Плюсы и минусы будут примерно такие же как у Swoole. Придется биндится на АПИ модуля и собирать его с nginx (php встраивается). Работать будет быстрее, модифицировать будет сложнее.

                        0
                        yii2 и symfony рядом по производительности с phalcon?
                        рукалицо, да в каких розовых снах вы такое увидели?)
                          0

                          Ссылки на бенчмарки доступны в статье, самим удивительно. Вот последние результаты: https://www.techempower.com/benchmarks/#section=test&runid=c7013ea3-22c9-44c1-864f-949509d78e0c&hw=ph&test=fortune&l=zik073-1r&c=6


                          Само собой синтетика не показатель, но результаты не в пользу Phalcon.

                            0
                            В вашем тесте результаты:
                            yii2 | laravel | symfony | phalcon | spiral
                            14,984 | 5,654 | 7,056 | 12,430 | 34,644

                            а в оригинальном:
                            13,530 | 5,484 | 1,213 | 23,318 | 0

                            Если небольшое отличие для yii2 и laravel можно назвать погрешностью, улучшенные результаты symfony оптимизацией за последние полгода (с момента последнего официального пересчёта рейтинга), то просадку phalcon почти в 2 раза никак не объяснить. Что собственно и вызвало удивление у stanlee и у меня.

                            Я правильно понимаю, что ваши тесты были добавлены в код оригинального бенчмарка только относительно недавно (пару месяцев назад) и вы займёте какое-то место в общем рейтинге только где-то через полгода?
                            Тогда если ваши текущие результаты нанести на текущий общий рейтинг, то выглядеть он будет приблизительно так:



                            т.е. в среднем в два раза быстрее yii2, laravel, symfony и phalcon, и в 10 раз медленнее чем workerman и swoole.

                            Что ж, вам есть куда расти. Не останавливайтесь на достигнутом. Если до swoole, который на C++/C, будет сложно угнаться, то достигнуть результатов workerman, который написан на чистом PHP, это уже вполне реально.
                            У вас есть ещё полгода до общего пересчёта рейтинга, чтобы успеть показать ещё лучшие результаты.
                              0
                              Я правильно понимаю, что ваши тесты были добавлены в код оригинального бенчмарка только относительно недавно (пару месяцев назад) и вы займёте какое-то место в общем рейтинге только где-то через полгода?

                              Все верно, ждем следующий раунд.


                              Если до swoole, который на C++/C, будет сложно угнаться, то достигнуть результатов workerman, который написан на чистом PHP, это уже вполне реально.

                              Я не уверен что в принципе возможно обогнать чистый Swoole на полноценном фреймворке с ORM и шаблонами. Если смотреть на пример Symfony+Swoole то отставание на 29%, а не в 10 раз. До самого дальнего фреймворка на Swoole/Workerman отставание в 4-5 раз, чего трудно добиться без жертв со стороны архитектуры.

                                0
                                Я не уверен что в принципе возможно обогнать чистый Swoole на полноценном фреймворке с ORM и шаблонами.
                                Судя по всему в следующем раунде мы так же увидим swoft (фреймворк на основе swoole). Его код уже присутствует в бенчмарке. Будем ждать пересчёта. Будет на кого ориентироваться.
                                  0

                                  Swoft уже есть, ориентировочно в два раза быстрее спирали.

                                    +1
                                    Судя по всему в следующем раунде мы так же увидим swoft (фреймворк на основе swoole). Его код уже присутствует в бенчмарке. Будем ждать пересчёта. Будет на кого ориентироваться.

                                    Стоит упомянуть, что Swoft/Swoole асинхронные, в отличие от, так что в реальных приложениях при должном использовании они всегда будут быстрее.
                                      0
                                      То что они будут быстрее никто и не сомневался :)
                                      Вопрос «насколько сильно».
                                      Например, workerman на тестах медленнее swoole в два раза, а на моих задачах «всего» на 10-20%, но мне гораздо приятнее работать с workerman, чем со swoole, поэтому я готов на такие «жертвы», лишь бы не читать документацию на китайском, спрашивать у разрабов, за что отвечает тот или иной магический параметр, у которого нет описания даже на китайском и т.д. С workerman я могу в любой момент залезть в код и разобраться что там, и как и даже что-то поправить и отправить пуллреквест.
                                  0

                                  Только оригинальный репозиторий вот этот — https://github.com/TechEmpower/FrameworkBenchmarks

                                    0
                                    Чёрт, перешёл по второй ссылке из вашей статьи:
                                    Полные бенчмарки доступны тут и тут.
                                    Наверное вам в статье, тоже стоит поправить.
                                      0

                                      Имелось ввиду что там два набора бенчмарков. Это разные проекты.

                                        0
                                        Почему-то решил, что раз первая ссылка — результат бенчмарка, то вторая ссылка — код того же фреймворка, а не какого-то другого.
                                        Теперь понятно, что перепутал.
                              +1
                              Скажу в контексте RoadRunner, а не Spiral, от последнего использую только Spiral\RoadRunner\PSR7Client и Spiral\Goridge\StreamRelay.

                              Спасибо большое за вашу работу. Реквестирую расширить документацию к RoadRunner — реально по крохам между страницами приходится понимать базовые вещи (те же golang middlewares). Такое ощущение, что писатель документации взял за цель минимизировать количество текста, оставив только чистую эссенцию новой информации.

                              Ещё бы реквестировал доку или пример работы не через php-cli, а через php-cgi в командной строке. В теории оно должно работать, на практике пока сложновато продраться через дебри.

                              Ещё вопрос, почему внутри используется zend diactaros, а не laminas diactaros? То есть ругающийся composer update иногда настораживает.

                              Ещё вопрос, а возможно ли заменить тот же diactaros на другой? например, более быстрый Nyholm\Psr7?

                              Ещё вопрос, а не рассматривали подход с fasthhttp? В теории, так как воркеров ограниченное количество, можно сделать ± zero memory allocation работу, или хотя бы минимизировать это дело.
                                0
                                Ну и ещё вопрос: а можно ли STDIN и STDOUT пайпы (через которые гоняют данные PHP и golang) приспособить для вызова RPC методов? Я так понимаю, эти пайпы самый быстрый способ обмена между ними, быстрее чем линукс-сокеты или обычные локальные сетевые соединения?..

                                В теории go-часть может ждать в Stdout первый бит или байт, если он допустим, «1» — это означает, что сейчас придёт response, если он «2» — сейчас придёт вызов RPC от PHP части, надо будет обработать и передать ответ назад, а PHP часть в свою очередь будет считывать свой STDIN.
                                  +1

                                  Можно, но для этого придется модифицировать протокол. Поддержка асинхронных клиентов задумана в 3й версии.

                                    0
                                    Ну и ещё вопрос: а можно ли STDIN и STDOUT пайпы (через которые гоняют данные PHP и golang) приспособить для вызова RPC методов? Я так понимаю, эти пайпы самый быстрый способ обмена между ними, быстрее чем линукс-сокеты или обычные локальные сетевые соединения?..


                                    Unix-сокеты довольно шустрые.

                                    Если для вашей задачи настолько важна производительность этой части — то есть смысл задуматься о смене языка PHP на что-то шустрое (тот же Go).

                                    Но, практически, здесь socket/local net/etc не является узким местом. СУБД куда как более значительный тормоз вашей системы.

                                    В теории go-часть может ждать в Stdout первый бит или байт, если он допустим, «1» — это означает, что сейчас придёт response, если он «2» — сейчас придёт вызов RPC от PHP части, надо будет обработать и передать ответ назад, а PHP часть в свою очередь будет считывать свой STDIN.

                                    STDIN/STDOUT — это неудобно для мультиплексирования, но, да — возможно.
                                    Посмотрите, к примеру, как реализован протокол WebSocket. Если отвлечься от того, что это сетевой протокол, то внутри он реализует обмен по одному потоку.
                                      0
                                      Я задумывался о теоретической возможности делать запросы к СУБД из golang, посылая результат в PHP, и тут как раз могут быть относительно большие потоки данных. Но да, вопрос больше теоретический.

                                      (Отвечая на вопрос «зачем» — экспериментирую, можно ли сделать общий кеш запросов между всеми воркерами).
                                        0
                                        Я задумывался о теоретической возможности делать запросы к СУБД из golang, посылая результат в PHP

                                        Тормозит сама СУБД.
                                        А не связь с ней из Go или PHP. Поэтому без разницы.
                                    0

                                    Привет,


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

                                    Ещё бы реквестировал доку или пример работы не через php-cli, а через php-cgi в командной строке. В теории оно должно работать, на практике пока сложновато продраться через дебри.
                                    CGI интеграцию еще не делали.

                                    Ещё вопрос, почему внутри используется zend diactaros, а не laminas diactaros? То есть ругающийся composer update иногда настораживает.
                                    В процессе переключения, ничего не хотим сломать.

                                    Ещё вопрос, а возможно ли заменить тот же diactaros на другой? например, более быстрый Nyholm\Psr7?
                                    Без проблем, фреймворк как раз и использует Nyholm по умолчанию.

                                    Ещё вопрос, а не рассматривали подход с fasthhttp? В теории, так как воркеров ограниченное количество, можно сделать ± zero memory allocation работу, или хотя бы минимизировать это дело.
                                    Есть в пайплайне, но большого прироста не ожидаем так как боттлнек на стороне PHP.
                                      0
                                      фреймворк как раз и использует Nyholm по умолчанию

                                      Спасибо, покопался, отвечу сам себе и тем, кто зайдёт сюда через год:
                                      $psr7 = new Spiral\RoadRunner\PSR7Client(new Spiral\RoadRunner\Worker($relay), new \Nyholm\Psr7\Factory\Psr17Factory());
                                      

                                      +1
                                      Ещё вопрос, почему внутри используется zend diactaros, а не laminas diactaros? То есть ругающийся composer update иногда настораживает.

                                      Войдет в 1.7.1 релиз. :)

                                      0
                                      Добрый день.
                                      Не нашел в обсуждениии, но очень интересно — в 20 году планируется выход PHP8, RoadRunner и Spiral будут с ним работать? Наверное больше интересует будет ли корректно взаимодействовать RoadRunner с кодом на PHP8?
                                        0

                                        Добрый день. Не вижу ни одной причины почему код может не работать, с 7.4 у нас проблем нет, старую магию давно выпилили.

                                          0
                                          Т.е. сервер RoadRunner который написан на Go запускает PHP интерпритатор который установлен на машине где он запускается? Т.е. если я в конце года, когда выйдет PHP8, установлю новый php и RoadRunner будет с ним работать, но код так же должен быть написан на php8, а фреймворк spiral может отвалиться т.к. он написан под php7?
                                            0

                                            Spiral это PHP фреймворк, к моменту выхода PHP8 мы будем поддерживать эту версию.

                                        +1
                                        Как любитель новых веяний в webDev — в противовес тяжеловесным традициям — какое то время присмативаюсь к spiral стэку. Пардон, не скажу про себя особо высоко как о знатоке backend языков/инструментов/паттернов.., но, суть, имя целью: 1) Обучение основам (и дальше/больше) вэб технологий в своём (пока) городе, в ср. азии, где население +-500тыс. 2) Смастерить вэб приложение административного (enterprise) типа для информ.оснащения некоего хозяйства (где отсутствие оперативной инфы встаёт в копеечку ), тут — к слову — создатели сети вилгуд автосервисов послужили эталоном…
                                        В общем, мне со своей колокольни видеться, что: а) spiral фреймворк — детище принципально новое, где в придачу go-lang реально в пользу. б) Если при этом бенчмарки не врут и на деле значительный выигрыш в производительности + сделан акцент на упрощение разработки (сравнивая с тем же symfony, сам больше приверженец модульности). в) В окружении семейный ряд инструментов (RR!!, ORM, Toolkit etc.) для покрытия основной части нужд для полноценной фулстэк работы,
                                        то определенно фреймворк достоен внимания, хотя и пока — понятно — без популярности дедов, но, хочу отметить, тут номером 1 стоит вопрос о полноте документации, и чтобы даже зеленый php ламер мумел по ней найти путь…
                                        Честно, пока толком руки не доходят до практико-изучения сего инструмента, но, есть планы в скором засучить рукава, будем видеть :))

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