---
title: "Производительность"
description: "По умолчанию FrankenPHP стремится предложить хороший компромисс между производительностью и простотой использования. Однако, используя подходящую конфигурацию, можно существенно улучшить производительность."
sidebar:
  label: "Оптимизация производительности"
  order: 2
seo:
  description: "По умолчанию FrankenPHP стремится предложить хороший компромисс между производительностью и простотой использования. Однако, используя подходящую…"
---
## Количество потоков и воркеров

По умолчанию FrankenPHP запускает в 2 раза больше потоков и воркеров (в режиме воркера), чем доступное количество ядер процессора.

Подходящие значения сильно зависят от того, как написано ваше приложение, что оно делает, и от вашего оборудования. Мы настоятельно рекомендуем изменить эти значения. Для лучшей стабильности системы рекомендуется, чтобы `num_threads` x `memory_limit` < `available_memory`.

Чтобы найти подходящие значения, лучше всего провести нагрузочные тесты, имитирующие реальный трафик. [k6](https://k6.io) и [Gatling](https://gatling.io) являются хорошими инструментами для этого.

Чтобы настроить количество потоков, используйте опцию `num_threads` директив `php_server` и `php`. Для изменения количества воркеров используйте опцию `num` секции `worker` директивы `frankenphp`.

### `max_threads`

Хотя всегда лучше точно знать, как будет выглядеть ваш трафик, реальные приложения, как правило, более непредсказуемы. [Конфигурация](/ru/docs/config#конфигурация-caddyfile) `max_threads` позволяет FrankenPHP автоматически создавать дополнительные потоки во время выполнения до указанного предела. `max_threads` может помочь вам определить, сколько потоков требуется для обработки вашего трафика, и может сделать сервер более устойчивым к скачкам задержки. Если установлено значение `auto`, лимит будет оценен на основе `memory_limit` в вашем `php.ini`. Если это невозможно, `auto` вместо этого будет по умолчанию равно 2x `num_threads`. Имейте в виду, что `auto` может сильно недооценивать необходимое количество потоков. `max_threads` похожа на [pm.max_children](https://www.php.net/manual/install.fpm.configuration.php#pm.max-children) в PHP FPM. Основное отличие состоит в том, что FrankenPHP использует потоки вместо процессов и автоматически распределяет их между различными скриптами воркеров и 'классическим режимом' по мере необходимости.

## Режим воркера

Включение [режима воркера](/ru/docs/worker) значительно улучшает производительность, но ваше приложение должно быть адаптировано для совместимости с этим режимом: необходимо создать скрипт воркера и убедиться, что приложение не имеет утечек памяти.

## Избегайте использования musl

Вариант официальных Docker-образов для Alpine Linux и предоставляемые нами бинарные файлы по умолчанию используют [библиотеку musl libc](https://musl.libc.org).

Известно, что PHP [работает медленнее](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381) при использовании этой альтернативной библиотеки C вместо традиционной библиотеки GNU, особенно при компиляции в режиме ZTS (потокобезопасном режиме), который требуется для FrankenPHP. Разница может быть существенной в сильно многопоточной среде.

Кроме того, [некоторые ошибки проявляются исключительно при использовании musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl).

В производственных средах мы рекомендуем использовать FrankenPHP, скомпилированный с glibc и с соответствующим уровнем оптимизации.

Этого можно достичь, используя Docker-образы Debian, используя [пакеты .deb, .rpm или .apk от наших мейнтейнеров](https://pkgs.henderkes.com) или [компилируя FrankenPHP из исходников](/ru/docs/compile).

Для более компактных или безопасных контейнеров вы можете рассмотреть [усиленный образ Debian](/ru/docs/docker#hardening-images) вместо Alpine.

## Настройка среды выполнения Go

FrankenPHP написан на языке Go.

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

Вероятно, вы захотите установить переменную окружения `GODEBUG` в значение `cgocheck=0` (по умолчанию в Docker-образах FrankenPHP).

Если вы запускаете FrankenPHP в контейнерах (Docker, Kubernetes, LXC...) и ограничиваете память, доступную для контейнеров, установите переменную окружения `GOMEMLIMIT` в значение доступного объёма памяти.

Для получения более подробной информации [страница документации Go, посвященная этой теме](https://pkg.go.dev/runtime#hdr-Environment_Variables), обязательна к прочтению, чтобы максимально эффективно использовать среду выполнения.

## `file_server`

По умолчанию директива `php_server` автоматически настраивает файловый сервер для обслуживания статических файлов (ресурсов), хранящихся в корневой директории.

Эта функция удобна, но имеет издержки. Чтобы отключить её, используйте следующую конфигурацию:

```caddyfile
php_server {
    file_server off
}
```

## `try_files`

Помимо статических файлов и файлов PHP, `php_server` также попытается обслужить индекс вашего приложения и индексные файлы директорий (`/path/` -> `/path/index.php`). Если вам не нужны индексы директорий, вы можете отключить их, явно определив `try_files` следующим образом:

```caddyfile
php_server {
    try_files {path} index.php
    root /root/to/your/app # явное указание корневой директории здесь обеспечивает лучшее кеширование
}
```

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

```caddyfile
route {
    php_server { # используйте "php" вместо "php_server", если вам вообще не нужен файловый сервер
        root /root/to/your/app
        worker /path/to/worker.php {
            match * # отправить все запросы напрямую воркеру
        }
    }
}
```

Альтернативный подход с 0 ненужных операций файловой системы заключается в использовании директивы `php` и разделении файлов от PHP по пути. Этот подход хорошо работает, если все ваше приложение обслуживается одним входным файлом. Пример [конфигурации](/ru/docs/config#конфигурация-caddyfile), которая обслуживает статические файлы за папкой `/assets`, может выглядеть так:

```caddyfile
route {
    @assets {
        path /assets/*
    }

    # всё, что находится за /assets, обрабатывается файловым сервером
    file_server @assets {
        root /root/to/your/app
    }

    # всё, что не находится в /assets, обрабатывается вашим индексным или воркер PHP-файлом
    rewrite index.php
    php {
        root /root/to/your/app # явное указание корневой директории здесь обеспечивает лучшее кеширование
    }
}
```

## Плейсхолдеры

Вы можете использовать [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders) в директивах `root` и `env`. Однако это предотвращает кеширование значений и существенно снижает производительность.

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

## `resolve_root_symlink`

По умолчанию, если корневая директория документа является символьной ссылкой, она автоматически разрешается FrankenPHP (это необходимо для корректной работы PHP). Если корневая директория документа не является символьной ссылкой, вы можете отключить эту функцию.

```caddyfile
php_server {
    resolve_root_symlink false
}
```

Это улучшит производительность, если директива `root` содержит [плейсхолдеры](https://caddyserver.com/docs/conventions#placeholders). В остальных случаях прирост производительности будет минимальным.

## Логи

Логирование, безусловно, очень полезно, но, по определению, оно требует операций ввода-вывода и выделения памяти, что значительно снижает производительность. Убедитесь, что вы [правильно настроили уровень логирования](https://caddyserver.com/docs/caddyfile/options#log) и логируете только то, что необходимо.

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

FrankenPHP использует официальный интерпретатор PHP. Все обычные оптимизации производительности, связанные с PHP, применимы к FrankenPHP.

В частности:

- убедитесь, что [OPcache](https://www.php.net/manual/book.opcache.php) установлен, включён и настроен должным образом;
- включите [оптимизацию автозагрузки Composer](https://getcomposer.org/doc/articles/autoloader-optimization.md);
- убедитесь, что кеш `realpath` достаточно велик для нужд вашего приложения;
- используйте [предварительную загрузку](https://www.php.net/manual/opcache.preloading.php).

Для получения более подробной информации ознакомьтесь с [разделом документации Symfony, посвященным производительности](https://symfony.com/doc/current/performance.html) (большинство советов полезны, даже если вы не используете Symfony).

## Разделение пула потоков

Приложениям часто приходится взаимодействовать с медленными внешними службами, такими как API, который имеет тенденцию быть ненадежным при высокой нагрузке или постоянно отвечает в течение 10+ секунд. В таких случаях может быть полезно разделить пул потоков, чтобы иметь выделенные "медленные" пулы. Это предотвращает потребление медленными конечными точками всех ресурсов/потоков сервера и ограничивает параллелизм запросов, направляемых к медленной конечной точке, подобно пулу соединений.

```caddyfile
example.com {
    php_server {
        root /app/public # корень вашего приложения
        worker index.php {
            match /slow-endpoint/* # все запросы к /slow-endpoint/* обрабатываются этим пулом потоков
            num 1 # минимум 1 поток для запросов к /slow-endpoint/*
            max_threads 20 # при необходимости разрешить до 20 потоков для запросов к /slow-endpoint/*
        }
        worker index.php {
            match * # все остальные запросы обрабатываются отдельно
            num 1 # минимум 1 поток для остальных запросов, даже если медленные конечные точки начинают зависать
            max_threads 20 # при необходимости разрешить до 20 потоков для остальных запросов
        }
    }
}
```

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