Pull to refresh

RoadRunner vs OpenSwoole vs FrankenPHP с Laravel Octane

Reading time10 min
Views3.8K

На эту тему было опубликовано множество статей из различного рода источников и у многих заметил тенденцию когда каждый из трёх упомянутых технологий значительно лидирует по сравнению с остальными. Тем более что у многих показаны какие-то дикие результаты в виде 1к запросов в секунду...

И мне захотелось всё проверить своими руками...

OpenSwoole vs Swoole

Начну с рассказа почему выбрал именно OpenSwoole, а не Swoole.

Причина оказалась банально простой - Swoole не установился. Да, он скачался, да, выполнил make, но он не смог сам установиться в PHP запросив ручное копирование файла swoole.so и его указание в php.ini. Мне не хотелось этого делать. К тому же, OpenSwoole сделал всё сам, а, учитывая всё те же многочисленные статьи сравнения, их производительность идёт бок о бок друг с другом, так что...

Окружение

Сервер

  • Тип: VPS

  • Процессор: Intel Core Broadwell 2.6 GHz, 2 ядра (точную модель не знаю)

  • Оперативная память: 4Gb

  • Диск: SSD NVMe

  • Ось: Ubuntu 24.04

  • Nginx: 1.24.0 (используется ProxyPass)

  • PHP: 8.4.4

  • Ширина канала: 500 Mbps

  • Местоположение: Netherlands

Клиент

Клиент - это окружение, с которого будут запускаться стресс-тесты

  • Тип: Desktop

  • Процессор: Intel Core i9-11900F, 16 ядер

  • Оперативная память: 64Gb

  • Диск: SSD 525/500 R/W

  • Ось: Windows 11 Pro

  • PHP 8.4.4

  • Ширина канала: 750 Mbps

  • Местоположение: Россия

Приложение и зависимости

laravel new test && cd test

composer require --dev pestphp/pest-plugin-laravel pestphp/pest-plugin-stressless

Роут для проверки:

// routes/api.php
app('router')->get('test', TestController::class);

// TestController
class TestController
{
    public function __invoke()
    {
        return response()->noContent();
    }
}
APP_ENV = production
APP_DEBUG = false

Команды тестирования

Запуск тестов осуществлялся в 10 потоков по 5, 10 и 30 секунд следующими командами:

./vendor/bin/pest stress test.***/api/test --concurrency=10 --duration=5
./vendor/bin/pest stress test.***/api/test --concurrency=10 --duration=10
./vendor/bin/pest stress test.***/api/test --concurrency=10 --duration=30

Nginx

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    server_name test.***;

    server_tokens off;
    root /home/user/test.***/current/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

    location /index.php {
        try_files /not_exists @octane;
    }

    location / {
        try_files $uri $uri/ @octane;
    }

    location ~* \.(eot|otf|ttf|woff|woff2|png|jpg|css)$ {
        add_header Access-Control-Allow-Origin *;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  off;

    error_page 404 /index.php;
 
    location @octane {
        set $suffix "";
 
        if ($uri = /index.php) {
            set $suffix ?$query_string;
        }
 
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header SERVER_PORT $server_port;
        proxy_set_header REMOTE_ADDR $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
 
        proxy_pass http://127.0.0.1:9000$suffix;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/test.***/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/test.***/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = test.***) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    server_name test.***;

    listen [::]:80;
    listen 80;
    return 404; # managed by Certbot
}

Установка и запуск

RoadRunner

На момент публикации поста актуальные версии зависимостей Spiral, на которых производилось тестирование, следующие:

  • spiral/roadrunner 2024.3.4

  • spiral/roadrunner-cli 2.7.1

  • spiral/roadrunner-http 3.5.1

Supervisor

[program:test]
process_name=%(process_num)02d
command=php -d variables_order=EGPCS /home/user/test/current/artisan octane:start --host=127.0.0.1 --rpc-port=6001 --port=9000 --server=roadrunner
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=user
redirect_stderr=true
stdout_logfile=/home/user/test/shared/storage/logs/octane.log
stopwaitsecs=3600
stdout_logfile_maxbytes = 10MB
stdout_logfile_backups = 5

Загрузка исполняемого файла

composer require spiral/roadrunner-cli spiral/roadrunner-http

php artisan octane:install --server=roadrunner

Тестирование

Test Duration ............................................................................ 5.09 s
Test Concurrency ............................................................................. 10
Requests Count ....................................................... 102.04 reqs/s 519 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration ............................... 104.21.96.1, 104.21.64.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 91.25 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 99.9 % — including server processing time ....................................... 91.20 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms
Test Duration ........................................................................... 10.09 s
Test Concurrency ............................................................................. 10
Requests Count ...................................................... 100.65 reqs/s 1016 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration ............................... 104.21.96.1, 104.21.64.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 90.01 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 100.0 % — including server processing time ...................................... 90.00 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms
Test Duration ........................................................................... 30.08 s
Test Concurrency ............................................................................. 10
Requests Count ...................................................... 101.59 reqs/s 3056 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration ............................... 104.21.96.1, 104.21.64.1, (+ 12 more) 0.00 ms  
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 92.12 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 99.9 % — including server processing time ....................................... 92.04 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms

OpenSwoole

Supervisor

[program:test]
process_name=%(process_num)02d
command=php -d variables_order=EGPCS /home/user/test/current/artisan octane:start --host=127.0.0.1 --port=9000 --server=swoole
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=user
redirect_stderr=true
stdout_logfile=/home/user/test/shared/storage/logs/octane.log
stopwaitsecs=3600
stdout_logfile_maxbytes = 10MB
stdout_logfile_backups = 5

Загрузка исполняемого файла

apt update
apt install -y software-properties-common && add-apt-repository ppa:openswoole/ppa -y
apt install -y libpq-dev
apt install -y php8.4-openswoole

Тестирование

Test Duration ............................................................................ 5.08 s  
Test Concurrency ............................................................................. 10  
Requests Count ....................................................... 109.77 reqs/s 558 requests  
Success Rate ............................................................................ 100.0 %  
DNS Lookup Duration ............................... 104.21.64.1, 104.21.96.1, (+ 12 more) 0.00 ms  
TLS Handshake Duration .................................................................. 0.00 ms  
Request Duration ....................................................................... 86.75 ms  
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms  
— TTFB 100.0 % — including server processing time ...................................... 86.75 ms  
— Download 0.0 % .................................................. 0.00 MB/req 0.06 MB/s 0.00 ms
Test Duration ........................................................................... 10.08 s
Test Concurrency ............................................................................. 10
Requests Count ...................................................... 113.44 reqs/s 1144 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration ............................... 104.21.64.1, 104.21.96.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 84.89 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 99.8 % — including server processing time ....................................... 84.73 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.06 MB/s 0.00 ms
Test Duration ........................................................................... 30.08 s
Test Concurrency ............................................................................. 10
Requests Count ...................................................... 116.79 reqs/s 3513 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration ............................... 104.21.64.1, 104.21.96.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 81.73 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 99.9 % — including server processing time ....................................... 81.66 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms

FrankenPHP

Supervisor

[program:test]
process_name=%(process_num)02d
command=php -d variables_order=EGPCS /home/user/test/current/artisan octane:start --host=127.0.0.1 --admin-port=2019 --port=9000 --server=frankenphp
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=user
redirect_stderr=true
stdout_logfile=/home/user/test/shared/storage/logs/octane.log
stopwaitsecs=3600
stdout_logfile_maxbytes = 10MB
stdout_logfile_backups = 5

Загрузка исполняемого файла

php artisan octane:install --server=frankenphp

Тестирование

Test Duration ............................................................................ 5.08 s  
Test Concurrency ............................................................................. 10  
Requests Count ........................................................ 113.1 reqs/s 574 requests  
Success Rate ............................................................................ 100.0 %  
DNS Lookup Duration .............................. 104.21.112.1, 104.21.96.1, (+ 12 more) 0.00 ms  
TLS Handshake Duration .................................................................. 0.00 ms  
Request Duration ....................................................................... 84.14 ms  
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms  
— TTFB 99.9 % — including server processing time ....................................... 84.09 ms  
— Download 0.0 % .................................................. 0.00 MB/req 0.06 MB/s 0.00 ms
Test Duration ........................................................................... 10.08 s
Test Concurrency ............................................................................. 10
Requests Count ......................................................... 111 reqs/s 1119 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration .............................. 104.21.112.1, 104.21.96.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 87.20 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 100.0 % — including server processing time ...................................... 87.17 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms
Test Duration ........................................................................... 30.08 s
Test Concurrency ............................................................................. 10
Requests Count ...................................................... 116.16 reqs/s 3494 requests
Success Rate ............................................................................ 100.0 %
DNS Lookup Duration .............................. 104.21.112.1, 104.21.96.1, (+ 12 more) 0.00 ms
TLS Handshake Duration .................................................................. 0.00 ms
Request Duration ....................................................................... 84.35 ms
— Upload 0.0 % .................................................... 0.00 MB/req 0.00 MB/s 0.00 ms
— TTFB 99.8 % — including server processing time ....................................... 84.22 ms
— Download 0.0 % .................................................. 0.00 MB/req 0.05 MB/s 0.00 ms

Результаты

Для сравнения были построены два графика: среднее время и количество запросов в секунду.

Как показала практика, RoadRunner самый медленный из трёх подопытных. Его просадка составляет около 6-10 мс на запрос и около -10 запросов в секунду по сравнению с двумя другими. А вот OpenSwoole идёт рядом с FrankenPHP.

Установка

Самая простая установка у FrankenPHP - его скачивает сам Laravel Octane и не требует вообще никаких дополнительных зависимостей.

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

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

Заключение

Какой из них использовать для своих нужд - определяйте исходя из своего проекта. Тесты проводились на "чистой" свеже-установленной версии Laravel. В случае наличия какой-либо логики поведение не сильно, но может отличаться, так как всё зависит от того, какие классы Octane будет "прогревать", какие запоминать, а какие инициализировать в процессе. Вы можете с лёгкостью проверить это на своих кейсах.

Only registered users can participate in poll. Log in, please.
Что используете с Laravel Octane?
9.71% FrankenPHP10
20.39% RoadRunner21
16.5% Swoole17
0.97% OpenSwoole1
23.3% Не использую Octane (планирую)24
20.39% Не использую Octane (не планирую)21
8.74% Не использую Octane (не знал о нём)9
103 users voted. 21 users abstained.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 12: ↑11 and ↓1+14
Comments24

Articles