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

Сам я давно работаю с nginx-unit (+ lumen), однако при получении новых проектов очень часто вижу использование php-fpm. На предложения перейти на nginx-unit, возникает вполне логичный вопрос - "а чем он лучше?". Поиск соответствующих статей в интернете дал мало результатов, в основном это достаточно старые и малоинформативные статьи на зарубежных сайтах, а так же статья от 2019 года на хабре, где проводилось аналогичное сравнение нескольких веб-серверов для приложения на базе symfony. Но, во-первых, php и nginx-unit уже ушли далеко вперед за время, прошедшее после публикации статьи, а во-вторых, меня интересовала именно производительность в связке с laravel и lumen. В связи с этим я собрал простейший тестовый стенд из нескольких веб-серверов и сделал небольшое нагрузочное тестирование, результатами которого и хочу поделиться.

О тестовом стенде

Характеристики тестового стенда:

  • CPU: AMD Ryzen 9 5900X 12-Core

  • RAM: DDR4 4000 MHz 32GiB x2

  • SSD: Samsung SSD 980 PRO 500GB nvme

  • OS: xubuntu 20.04

Приложения для тестирования:

  • Laravel 8.69.0

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

  • Lumen 8.3.1

    Облегченная версия Laravel, созданная в основном для реализации api-приложений. В части функциональности сохранил все самые важные компоненты Laravel, а то, что не является критически важным (например, фасады), урезано. При этом он более производителен чем Laravel, но далеко не все задачи на нем решать выгодно. Как это обычно бывает, для каждой конкретной задачи есть свой оптимальный инструмент. Для одних задач выгоднее использовать laravel, для других lumen, для третьих php использовать не выгодно вовсе.

Веб серверы:

  • Php-fpm

    Стандартный менеджер php процессов. Для каждого запроса требуется инициализация фреймворка.

  • Nginx unit

    Веб-сервер приложений, разработанный командой nginx. Для каждого запроса требуется инициализация фреймворка.

  • Laravel-octane

    Строго говоря, это не веб-сервер, а пакет управления приложением от команды laravel. А вот под капотом использует веб-сервер Swoole или RoadRunner . Фреймворк инициализируется при первом запросе, далее хранится в памяти и не реинициализируется при последующих запросах.

Версия php во всех сборках: 8.0.12

Инструменты для тестирования:

  • Yandex tank для проведения нагрузочного тестирования. Используется самый свежий из доступных docker-образ.

  • Telegraf (входит в набор инструментов yandex tank) для сбора статистики по используемым приложениями ресурсам компьютера.

  • Overload (так же из комплекта yandex tank) для построения графиков.

Конфигурация

Приложения

Все приложения имеют стандартную конфигурацию. Логирование ошибок отключено. Кэш собирается при первом обращении только для компонентов laravel/lumen (таких, как роутер и т.п.), для контроллеров кэша нет. У приложения 1 эндпоинт, который при обращении обрабатывает реквест, и возвращает встроенный json респонс, содержащий случайное число, параметр конфигурации, статическую строку и заголовки запроса. Пример ответа сервиса:

{
    "app_env": "prod",
    "type": "unit-lumen",
    "number": 1527706674,
    "headers": {
        "accept-language": [
            "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"
        ],
        "accept-encoding": [
            "gzip, deflate"
        ],
        "accept": [
            "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9"
        ],
        "user-agent": [
            "Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/93.0.4577.99 Safari\/537.36"
        ],
        "upgrade-insecure-requests": [
            "1"
        ],
        "connection": [
            "keep-alive"
        ],
        "host": [
            "10.100.9.3"
        ],
        "content-length": [
            ""
        ],
        "content-type": [
            ""
        ]
    }
}

Все приложения поднимаются в docker-контейнерах. Во всех приложениях используется кастомный php.ini, со следующими параметрами:

upload_max_filesize = 50M
post_max_size = 50M
opcache.enable=1
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=100000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.max_wasted_percentage=10
apc.enable_cli=1
memory_limit=256M

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

Для тестирования php-fpm используется дополнительный контейнер с nginx, поскольку php-fpm работает через протокол FastCGI. Используется официальный образ nginx:1.19.6-alpine.

Конфигурация nginx

nginx.conf

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  2048;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile       on;
    tcp_nopush     on;
    resolver_timeout 10s;
    server_tokens off;
    keepalive_timeout 3;
    reset_timedout_connection on;
    client_body_timeout 2;
    send_timeout 1;
    server_names_hash_bucket_size 128;
    client_max_body_size 32m;
    proxy_buffers 4 512k;
    proxy_buffer_size 256k;
    proxy_busy_buffers_size 512k;

    gzip on;
    gzip_comp_level 9;
    gzip_disable "msie6";
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;

    include /etc/nginx/conf.d/*.conf;
}

default.conf

server {
    listen       80;
    server_name  localhost;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        fastcgi_pass 10.100.9.100:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /var/www/public/$fastcgi_script_name;
    }
}

Подопытные кролики, они же собранные контейнеры

1. nginx + php-fpm + laravel

конфигурация

Php-fpm: используется официальный образ php:8.0.12-fpm

Изменения php.ini:

memory_limit=512M
pm = static
pm.max_children = 100
pm.max_requests = 1000

Тут сразу стоит оговориться: изначально планировалось что каждому веб-серверу будет выделено 16 воркеров по числу логических ядер (об этом ниже), но с php-fpm возникли проблемы в процессе тестирования, пришлось выдавать ему больше воркеров.

2. nginx + php-fpm + lumen

Конфигурация полностью идентична "nginx + php-fpm + laravel"

3. nginx-unit + laravel

Так вышло, что немногим ранее я уже собирал контейнер с nginx-unit для php8, поскольку нормально работающих контейнеров, отвечающих моим производственным задачам не было. Не долго думая, именно этот контейнер я и использовал как основу, чуть доработав под текущее тестирование. Можно заметить, что сборка производилась на базе ubuntu:hirsute, поскольку с apline я не очень дружу, да и разбираться тогда времени с ним не было. Нужна была сборка здесь и сейчас, при этом не планировалось что она дойдет до прода в первозданном виде. Хотя, учитывая результаты тестов, теперь может и дойти.

конфигурация

Параметры nginx-unit:

{
  "listeners": {
    "*:80": {
      "pass": "routes"
    }
  },
  "routes": [
    {
      "match": {
        "uri": [
          "*.manifest",
          "*.appcache",
          "*.html",
          "*.json",
          "*.rss",
          "*.atom",
          "*.jpg",
          "*.jpeg",
          "*.gif",
          "*.png",
          "*.ico",
          "*.cur",
          "*.gz",
          "*.svg",
          "*.svgz",
          "*.mp4",
          "*.ogg",
          "*.ogv",
          "*.webm",
          "*.htc",
          "*.css",
          "*.js",
          "*.ttf",
          "*.ttc",
          "*.otf",
          "*.eot",
          "*.woff",
          "*.woff2",
          "/robot.txt"
        ]
      },
      "action": {
        "share": "/var/www/public"
      }
    },
    {
      "action": {
        "pass": "applications/php"
      }
    }
  ],
  "applications": {
    "php": {
      "type": "php 8.0",
      "limits": {
        "requests": 1000,
        "timeout": 60
      },
      "processes": {
        "max": 16,
        "spare": 16,
        "idle_timeout": 30
      },
      "user": "www-data",
      "group": "www-data",
      "working_directory": "/var/www/",
      "root": "/var/www/public",
      "script": "index.php",
      "index": "index.php"
    }
  },
  "access_log": "/dev/stdout"
}

4. nginx-unit + lumen

Конфигурация полностью идентична "nginx-unit + laravel"

5. laravel-octane (swoole) + laravel

Добавить к тестируемым приложениям octane меня побудило сообщение Тейлора (автора laravel) в твиттере. Меня смутили его утверждения "октан быстрее люмена" и "немного увеличить скорость". В связи с чем к тестам был добавлен наскоро собранный контейнер с laravel-octane, работающим на основе swoole.

Laravel-octane поддерживает помимо swoole еще и roadranner, и изначально я планировал протестировать и его. Но как ни странно, мне не удалось заставить работать последний нормально даже на костылях внутри докера (если быть точнее, удалось, но автодеплой он не переживал, только ручной старт), в связи с чем в последствие я от него отказался. Тем не менее, swoole мне было достаточно. Кроме того, Laravel-octane+swoole добавляют дополнительные инструменты, которые могут пригодиться при решении ряда стоящих передо мной задач.

конфигурация

.env

OCTANE_SERVER=swoole

Все остальные параметры передаются прямо в команде запуска сервера:

php artisan octane:start --port=80 --workers=16 --max-requests=1000 --host=host.docker.internal

Yandex tank

Для тестирование было принято решение использовать 2 различных профиля нагрузки, отражающих 2 ситуации: "стандарт" для ежедневных часто встречающихся нагрузок, "стресс" для понимания границ работоспособности системы.

Профиль нагрузки "стандарт"

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

phantom:
  address: 10.100.9.101
  uris:
    - /
  load_profile:
    load_type: rps
    schedule: step(5, 100, 5, 10s) const(100, 2m30s)
  timeout: 2s
console:
  enabled: true
telegraf:
  config: 'monitoring-nginx-unit-laravel.xml'
  enabled: true
  kill_old: false
  package: yandextank.plugins.Telegraf
  ssh_timeout: 30s
overload:
  enabled: true
  package: yandextank.plugins.DataUploader
  token_file: 'overload_token.txt'
Профиль нагрузки "стресс"

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

phantom:
  address: 10.100.9.101
  uris:
    - /
  load_profile:
    load_type: rps # schedule load by defining requests per second
    schedule: line(1, 1000, 10m)
  timeout: 2s
console:
  enabled: true
telegraf:
  config: 'monitoring-nginx-unit-laravel.xml'
  enabled: true
  kill_old: false
  package: yandextank.plugins.Telegraf
  ssh_timeout: 30s
overload:
  enabled: true
  package: yandextank.plugins.DataUploader
  token_file: 'overload_token.txt'

Таймаут 2 секунды был выбран не случайно. Во-первых, в в api-приложениях если эндпоинт отвечает слишком долго, чаще всего это бывает фатально для работоспособности приложения (я часто применяю api-gateway в рамках микросервисной архитектуры, и в нем всегда жестко задаю таймаут на запрос в зависимости от задач от 1 до 3 секунд, не более. Но при этом, если эндпоинт действительно ворочает тяжелую логику - я всегда вытаскиваю подобные вещи в джобы). Во-вторых, при тестировании php-fpm yandex tank'у с дефолтным таймаутом 11 секунд не хватало ресурсов для обеспечения нагрузки, т.к. он слишком долго ожидал ответа (об этом далее).

Telegraf

С ним все просто, создана минимальная конфигурация для сбора данных о потребляемых ресурсах

Конфигурация
<Monitoring>
  <Host address="10.100.9.101" interval="1" username="root">
    <CPU/> <Kernel/> <Net/> <System/> <Memory/> <Disk/> <Netstat /> <Nstat/>
  </Host>
</Monitoring>

Нагрузочное тестирование и результаты

Для тестирования все участники действия были жестко ограничены по количеству логических ядер процессора:

  • Контейнер с приложением: 16 ядер

  • Nginx (для тестов с php-fpm): 2 ядра

  • Yandex tank: 4 ядра (на 2 ядрах нехватало ресурсов для работы с php-fpm даже с уменьшенным таймаутом)

  • Чрезвычайно важный и крайне приоритетный процесс проталкивания /dev/zero в /dev/null (вопросы излишни, мой личный костыль, чтобы упростить себе жизнь): строго 1 полностью загруженное ядро

  • Все процессы ОС: оставшееся 1 ядро, фактически свободное, про запас

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

Далее пойдут графики тестирования. Если нет графика ошибок - значит их не было.

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

Профиль нагрузки "стандарт"

1. nginx + php-fpm + laravel

Детальный отчет: https://overload.yandex.net/479324

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

2. nginx + php-fpm + lumen

Детальный отчет: https://overload.yandex.net/479325

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

3. nginx-unit + laravel

Детальный отчет: https://overload.yandex.net/479330

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

4. nginx-unit + lumen

Детальный отчет: https://overload.yandex.net/479332

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

5. octane (swoole) + laravel

Детальный отчет: https://overload.yandex.net/479339

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

Перцентили времени ответа (ms)

99%

98%

95%

90%

85%

80%

75%

50%

HTTP OK %

nginx + php-fpm + laravel

60

59

56

52

48

46

45

44

100

nginx + php-fpm + lumen

18

18

17

16

16

15

15

14

100

nginx-unit + laravel

7.6

7

6.5

5.8

5.3

5.2

5.2

4.059

100

nginx-unit + lumen

1.930

1.870

1.640

1.520

1.460

1.410

1.320

1.070

100

octane (swoole) + laravel

1.230

1.200

1.160

1.110

1.050

1.010

0.980

0.800

100

Профиль нагрузки "стресс"

1. nginx + php-fpm + laravel

Детальный отчет: https://overload.yandex.net/479202

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор
Сводка по ошибкам
Сводка по ошибкам
Детали тестирования этой сборки

Тестировать пришлось несколько раз. Первый раз, когда php-fpm было выделено 16 воркеров, а nginx имел настройки по умолчанию, вся эта сборка схлопнулась в черную дыру и на 120 rps начала орать об ошибках и недостаточном количестве воркеров (причем оба одновременно). После этого nginx был перенастроен на адекватную конфигурацию, а php было выделено 100 и 250 воркеров (2 теста, в итоге остановился на 100, для 250 не хватало логических ядер). Поскольку тестирование этим профилем нагрузки я проводил первым, в дальнейшем эта конфигурация и была использована на всех остальных тестах. Кроме того, я столкнулся с тем, что на отметке ~120 rps php-fpm резко перестает справляться с нагрузками, запросы зависают, и yandex tank с дефолтным таймаутом запроса в 11 секунд начинает загибаться, ему банально не хватает ресурсов для обеспечения требуемого количества запросов. В связи с этим я выделил танку 4 ядра (изначально было 2), и снизил таймаут до 2 секунд. На графике хорошо видна эта 2-секундная отсечка, когда php-fpm загнулся. Но выводы будут ниже.

2. nginx + php-fpm + lumen

Детальный отчет: https://overload.yandex.net/479210

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор
Сводка по ошибкам
Сводка по ошибкам

3. nginx-unit + laravel

Детальный отчет: https://overload.yandex.net/479184

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор

4. nginx-unit + lumen

Детальный отчет: https://overload.yandex.net/479207

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор
сводка по ошибкам

В процессе тестирования произошла 1 ошибка с http кодом 500 именно от самого приложения. В связи с отключенными логами, понять что именно случилось невозможно, и больше она не воспроизводилась при повторных прогонах. Однако, учитывая что на 300299 успешных запросов 1 оказался ошибочным, я посчитал что эту ошибку можно проигнорировать.

5. octane (swoole) + laravel

Детальный отчет: https://overload.yandex.net/479204

Время ответов сервера
Время ответов сервера
Нагрузка на процессор
Нагрузка на процессор
Сводка по ошибкам
Сводка по ошибкам

Перцентили времени ответа (ms)

* Звездочкой в таблице будут отмечены сборки на базе php-fpm, которые не выдержали нагрузки вообще. У них будет указана отсечка rps, после которой они начали умирать. Как следствие, перцентили даны до этой отсечки. Я решил все же добавить их в таблицу, хотя бы в таком виде.

Сборка на базе laravel-octane указана дважды. Полная статистика, когда сервер не выдержал нагрузок, и статистика до точки смерти. Как и с php-fpm - отмечена звездочкой с указанием порогового rps.

99%

98%

95%

90%

85%

80%

75%

50%

HTTP OK %

* nginx + php-fpm + laravel

~120 rps

62

59

55

52

49

47

45

43

-

* nginx + php-fpm + lumen

~400 rps

34

25

19

17

16

16

15

14

-

nginx-unit + laravel

6.6

6

5.5

5.2

4.96

4.7

4.5

3.79

100

nginx-unit + lumen

1.77

1.56

1.4

1.25

1.17

1.13

1.08

0.91

100

octane (swoole) + laravel

18

8.3

4.85

3.88

3.45

3.1

2.85

1.87

82.807

* octane (swoole) + laravel

~600 rps

3.23

3.01

2.85

2.55

2.2

2.029

1.92

0.78

-

Выводы

Итоги тестирования меня, надо признать, удивили. Я конечно ожидал, что php-fpm будет медленнее чем, nginx-unit, но не настолько: разница во времени генерации ответа почти в 10 раз. Так же меня удивило то, что php-fpm оказался единственным, который под стресс тестом захлебнулся вообще. Возможно, это связано с тем, что я не являюсь экспертом в конфигурации nginx и php, но остальные сервера, работавшие на том же конфиге, стресс тест выдержали, хоть и с потерями. В итоге, я пришел к выводу, что я не зря давно уже пересел на nginx-unit.

Что касается сравнения производительности: если не брать в расчет php-fpm, то nginx-unit единственный, кто выдержал стресс тест без потерь, хотя на octane я возлагал большие надежды. В связи с этим, php-fpm я дальше учитывать в своих рассуждениях не буду.

Скачки времени выполнения для 100% перцентиля наблюдаются и у nginx-unit и у octane+swoole, но в обоих случаях не сказать, чтобы они были катастрофическими, а причины их могут быть вовсе в том, что все тестирование произодилось на "слегка" не предназначенном для этого железе и окружении. Опять же, все тесты были сугубо синтетическими, и в реальных условиях, когда приложение будет помимо всего прочего и бизнес логику ворочать, и в базу и кэш ходить, ситуация будет иная.

Технически, octane будет быстрее в реальных условиях, за счет сохранения состояния приложения и всех соединений. Кроме того, у swoole есть собственные реализации высокопроизводительного кэша и некоторые другие "примочки", которые позволят ускорить работу тяжелой бизнес логики. Но есть и своя цена: во-первых, все упрется в то, что ему нужно больше оперативной памяти (я понимаю, что цена на оперативную память в дата-центрах не высока для бизнеса, но учитывать это стоит), а во-вторых, что еще важнее, octane крайне недружелюбен к говнокоду - ошибок он не прощает. На официальном сайте есть даже раздел с примерами реализации, работающими на других серверах, но которые приведут к неработоспособности приложения конкретно на octane.

С другой стороны, nginx-unit оказался более стабилен при высоких нагрузках. И при этом в синтетических тестах в связке с lumen почти не проигрывает octane, так что я не зря сомневался в словах Тейлора. Lumen еще поживет и продолжит удивлять (во всяком случае меня, я не ожидал от него такой прыти по сравнению с laravel).

В итоге для себя я сделал вывод, что в случае, если требуется предельно стабильный сервис, который будет жить даже там, где другие умирают, то это nginx-unit. А чтобы он жил очень хорошо, с ним все же выгодно использовать lumen, если речь об api. Если же требуется сервис, который должен быть предельно быстрым, и обсчитывать сложную бизнес логику на лету - то это octane, если того позволяют ресурсы (учитывая как оперативную память, так и то, что при ожидании высоких нагрузок, стоит позаботиться о балансировщике и горизонтальном масштабировании заранее). В данном случае octane является тем самым "оружием массового поражения", которое далеко не везде стоит применять, тут Тейлор абсолютно прав. Для каждой задачи - свой инструмент.

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

Комментарии (27)


  1. bashkarev
    12.11.2021 13:19
    +2

    Добавлю ложку дегтя к nginx-unit, у него есть свои большие плюсы но так же и минусы:

    • Дочерние процессы создаются изолировано и вся функциональность mmap а это (opcache, apcu, stream) будут потреблять в x раз больше оперативной памяти. (issues) но есть и плюс, изоляция дает большую стабильность при реактивной нагрузке

    • Отсутствие мониторинга, невозможно тонко рассчитать количество процессов (issues)


    1. bashkarev
      12.11.2021 13:48
      +1

      по 1 пункту скоро будут процессы прототипы https://github.com/nginx/unit/commit/e207415a78ae67b937faf7e5bcd6e5192993180a


  1. Dionisvl
    12.11.2021 13:24
    +1

    тут собрана интересная инфа сколько rps вообще может держать пых https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=fortune&l=zik073-sf&c=2


  1. miksir
    12.11.2021 14:39
    +4

    Тут стоит задать вопрос - будете ли вы nginx-unit сразу публиковать наружу в продакшн окружении? Или все же поставите nginx перед ним? А если поставите - то логично тестировать nginx + fpm vs nginx + nginx-unit


    1. VBart
      13.11.2021 02:15
      +3

      На самом деле наша (разработчиков Unit) цель - это довести его по функциональности до, как минимум, паритета с nginx, чтобы можно было без труда всегда выставлять его наружу. Сейчас его приходится ставить перед nginx в тех случаях, когда от nginx нужна некоторая дополнительная функциональность, которая пока ещё отсутствует в Unit.


      1. miksir
        13.11.2021 02:30
        +1

        Но задачу балансировки при более одной ноды решать все равно придется промежуточным сервером. Мой коммент скорее был, что тест в статье меряет не fpm vs unit, а накладные расходы реверс прокси.


        1. VBart
          13.11.2021 13:55
          +1

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


          1. miksir
            13.11.2021 15:26

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

            В предыдущем тесте как раз был nginx + fpm vs nginx + unit, и там вполне сопоставимые цифры были на пока не задрали конкурентность.


            1. VBart
              13.11.2021 16:14
              +2

              Вот только nginx там был совершенно не настроен на производительность и снижение задержек. В случае с Unit-ом можно было бы включить кэш keepalive соединений и выкрутить ручку в большое значение - это бы существенно снизило задержки без какого-либо негативного эффекта.

              А в случае с PHP-FPM такое нормально работать не может, т.к. каждое keepalive соединение будет удерживать занятым один рабочий процесс c PHP постоянно и он будет простаивать, ожидая запроса конкретно в данном соединении. И если все его рабочие процессы будут заняты keepalive соединениями, то ни одно дополнительное соединение вообще не будет обработано и получим пресловутый "504 Gateway Timeout", как только число одновременных запросов превысит число рабочих процессов PHP-FPM.


              1. borovinskiy
                13.11.2021 17:01

                И как nginx настраивать с PHP-FPM, если nginx еще и статика отдается?


                1. VBart
                  14.11.2021 21:18

                  Раздача статики тут не влияет. А PHP-FPM настройкой nginx-а не исправить.


    1. straykerwl Автор
      13.11.2021 15:02

      перед этими сборками будет стоять шина, т.е. они именно в том виде в котором будут за шиной находиться. В связи с этим тестировать nginx + nginx-unit смысла и нет.


      1. miksir
        13.11.2021 15:19

        Шина используется внутри пхп приложения. А если вы каким-то образом придумали прослойку вычитывающую шину и делающую запросы в пхп (хз зачем), то можно и fastcgi запросы делать.


  1. borovinskiy
    12.11.2021 15:45
    +1

    Preload использовался?


    1. straykerwl Автор
      13.11.2021 15:05

      нет, ни в одной из сборок


      1. borovinskiy
        13.11.2021 16:56

        Если уж пошла речь за производительность, то хорошо бы и с preload проверить и unite и php-fpm.

        На голой Symfony смотрел и 'hello world' ускорился с preload в 2 раза.


  1. Graid
    12.11.2021 19:41

    Жалко не добили с RR. По идеи должно было быть получше чем со swoole


  1. Fockker
    12.11.2021 23:10
    +1

    Выводы, если честно, несколько обескураживают.
    "Я хорошо разбираюсь в апельсинах, но до кучи решил ещё протестировать яблоки и бананы - взял какие под руку под руку попались. Ну в общем победили апельсины".

    К сожалению, я тоже не спец в настройке различных вариантов окружения, и не могу указать на какие-то конкретные недочёты (ну кроме разве что наличия preloading-а, который, по идее, мог немного подтянуть результаты "умирающих" сборок), но мне как раз очень интересно такое сравнение само по себе. Но вот это "я не спец в fpm, взял что попалось" и "octane меня тоже удивил" неизбежно наводят на мысль о том, что результаты могли бы быть и другими.

    Но зато я узнал из статьи о существовании Nginx-unit, пойду почитаю как это работает.


    1. borovinskiy
      13.11.2021 10:37
      +1

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

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


  1. Zolotov88
    12.11.2021 23:10
    +1

    Интересно было бы посмотреть ещё на Laravel + RoadRunner и желательно не через кривоватую реализацию через Octane, а через официальный инструмент от разработчиков rr. Мне кажется эта связка могла бы удивить не меньше.


  1. VBart
    13.11.2021 02:23
    +6

    Мы уделяем большое внимание производительности и масштабируемости при разработке Unit-а, поэтому результаты не удивляют. Но, безусловно, есть ещё куда расти, т.к. не все идеи пока ещё реализованы и в будущем версиях показатели будут только улучшаться.


  1. n_bogdanov
    13.11.2021 12:42
    +1

    Конфиги присутствуют, но где же код? Хочется получить код приложений и попробовать воспроизвести тестирование.


  1. mcroitor
    15.11.2021 12:10
    -1

    Какая настройка использовалась для PHP-FPM?

    Если по умолчанию, то тогда понятен плохой результат сборки: по умолчанию PHP-FPM поднимает максимум 20 потоков.

    1. PHP: Configuration - Manual

    2. How to Change the PHP-FPM max_children Setting - ServerPilot

    Надо первого игрока переконфигурировать и заново протестировать.


    1. Chupaka
      15.11.2021 17:13
      +4

      В статье указаны настройки PHP-FPM:

      memory_limit=512M
      pm = static
      pm.max_children = 100
      pm.max_requests = 1000

      И написано, почему значение было увеличено. Вы статью-то читали? :)


      1. mcroitor
        16.11.2021 09:03

        Был невнимателен.


  1. ReDev1L
    16.11.2021 13:52
    +1

    Не в обиду, но Ваш образ nginx-unit-php8 - поделка кулхацкера.

    Спасибо за тесты, я не думал насчёт nginx-unit, а roadrunner внедрять пока нет времени, на предыдущем проекте писали сразу под rr.

    @VBartспасибо за юнит, буду тестить, деплоить один юнит намного проще чем fpm+nginx.


  1. N0zzy
    25.11.2021 19:32

    А чего openlitespeed нет?