Non-Thread-Safe PHP: администрирование сервера с NTS
Основные принципы работы с PHP Non-Thread-Safe
PHP Non-Thread-Safe (NTS) - сборка интерпретатора, которая не поддерживает одновременное выполнение в нескольких потоках одного процесса. Такая версия предназначена для веб-серверов, использующих многопроцессную модель (например, Apache с MPM prefork или Nginx в связке с PHP-FPM). В отличие от Thread-Safe (TS) сборки, NTS не содержит механизмов синхронизации доступа к глобальным данным, что даёт прирост производительности до 10-30% и снижает потребление памяти.
Основное решение: при настройке веб-сервера на базе Apache (prefork) или Nginx+PHP-FPM следует использовать именно NTS версию PHP. Это обеспечивает максимальную скорость обработки запросов и предотвращает неожиданные сбои, связанные с состоянием гонок. Для проверки текущей сборки выполняется команда:
php -i | grep "Thread Safety"Mod rewrite index php (настройка mod_rewrite для index.php)
Если вывод содержит disabled - версия NTS. Если enabled - TS. Также можно воспользоваться PHP-скриптом:
<?php
echo PHP_ZTS ? 'Thread Safe' : 'Non-Thread Safe';
?>Upstream php fpm (настройка upstream для php-fpm в nginx)
Результат для NTS: Non-Thread Safe.
Типичная ошибка: при попытке использовать NTS сборку с модулем mod_php для Apache в режиме worker (потоковый) возникает нестабильная работа и фатальные ошибки. Решение: переключить MPM на prefork или использовать FastCGI (например, php-fpm).
Как определить сборку PHP без запуска интерпретатора?
Если сам PHP не запускается, можно посмотреть на имя файла интерпретатора. В Windows: php-8.2.nts-Win32-vs16-x64.zip (NTS) против php-8.2.ts-Win32-vs16-x64.zip (TS). В Linux - по конфигурации сборки, например, в выводе phpinfo() или командой:
php -r "echo PHP_ZTS ? 'TS' : 'NTS';"запустить локальный сервер php (запуск локального сервера php)
Проблема: в некоторых окружениях переменная PHP_ZTS не определена (если PHP собран без поддержки ZTS). Фактически её отсутствие означает NTS.
Как собрать PHP из исходников с отключённой потокобезопасностью?
Для компиляции используется флаг --disable-zts. Пример команды конфигурации:
./configure --prefix=/usr/local/php-nts --with-apxs2=/usr/bin/apxs --with-mysqli --enable-fpm --disable-ztsPhp non thread safe (php non-thread-safe (не потокобезопасная версия))
После этого выполняются make и make install. Важно: если не указать --disable-zts, по умолчанию может быть включена ZTS (зависит от версии).
Ошибка: при сборке расширения для NTS вручную (например, через phpize) необходимо передать тот же флаг компиляции. Если этого не сделать, расширение загрузится с предупреждением "Unable to load dynamic library". Решение: при запуске phpize добавить флаг --disable-zts, либо пересобрать расширение сокета с соответствующими опциями.
Что делать, если расширение не загружается после перехода на NTS?
Расширения, собранные для TS, несовместимы с NTS. Лог ошибок может содержать строку:
PHP Warning: PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /usr/lib/php/20200930/pdo_mysql.so (/usr/lib/php/20200930/pdo_mysql.so: undefined symbol: compiler_globals))Rewritecond request filename php (правило rewritecond для php в .htaccess)
Решение: загрузить версию расширения, собранную под NTS. Например, в репозиториях дистрибутивов пакеты PHP часто разделены: php (NTS) и php-zts (TS). Установка соответствующих пакетов:
# Ubuntu/Debian (NTS)
sudo apt install php-cli php-fpm php-mysql
# Для TS
sudo apt install php-zts php-zts-fpm php-zts-mysqlApache 2.4 php 8.2 (настройка apache 2.4 с php 8.2)
Если расширение разрабатывается самостоятельно, его нужно перекомпилировать с теми же флагами, что и основной PHP. Пример:
cd /path/to/extension
phpize
./configure --with-php-config=/usr/local/php-nts/bin/php-config
make && sudo make installPhp index сервер (настройка индексного файла php на сервере)
Как настроить веб-сервер для работы с NTS?
Рекомендуется использовать FastCGI-прокси (PHP-FPM). Пример конфигурации Nginx для передачи запросов:
server {
listen 80;
server_name example.com;
root /var/www/html;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}Var www html index php (размещение index.php в var/www/html)
Для Apache с mod_proxy_fcgi:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html
<FilesMatch \.php$>
SetHandler "proxy:unix:/var/run/php/php8.2-fpm.sock|fcgi://localhost"
</FilesMatch>
</VirtualHost>
Возможная ошибка: если PHP-FPM слушает на TCP-порту, а не Unix-сокете, в конфигурации нужно указать соответствующий адрес. В логах Nginx может появиться "connect() failed (111: Connection refused)". Проверка: sudo service php8.2-fpm status.
Можно ли использовать NTS с многопоточным веб-сервером (Apache worker или event)?
Нет, это не рекомендуется. В многопоточном окружении один процесс PHP может выполняться одновременно в нескольких потоках, что приводит к повреждению данных. Необходимо применять Thread-Safe версию. Для NTS подходят только модели, где каждый запрос обрабатывается отдельным процессом (prefork, php-fpm).
Проблема: при попытке запустить NTS в многопоточном Apache возможны segmentation fault или необъяснимые глюки. Решение: переключить MPM или перейти на Nginx+PHP-FPM.
Расширенный пример проверки сборки PHP с использованием phpinfo():
<?php
phpinfo();
?>
В выводе, после открытия в браузере, ищем строку Thread Safety. Для NTS отображается "disabled".
Thread Safety => enabled (TS) - или - Thread Safety => disabled (NTS)
Пример компиляции PHP 8.2 с флагом --disable-zts и включением FPM:
wget https://www.php.net/distributions/php-8.2.12.tar.gz
tar -xzf php-8.2.12.tar.gz
cd php-8.2.12
./configure --prefix=/usr/local/php-nts \
--enable-fpm \
--with-mysqli \
--with-pdo-mysql \
--disable-zts \
--with-openssl \
--enable-bcmath
make -j$(nproc)
sudo make install
После установки проверка:
/usr/local/php-nts/bin/php -i | grep "Thread Safety"
Thread Safety => disabled
Демонстрация ошибки при загрузке расширения, собранного для TS, в NTS PHP:
php -m 2>&1 | grep -i error
PHP Warning: PHP Startup: Unable to load dynamic library 'imagick' (tried: /usr/lib/php/20200930/imagick.so (/usr/lib/php/20200930/imagick.so: undefined symbol: zend_new_interned_string)) in Unknown on line 0
Решение: переустановить пакет imagick из репозитория NTS или собрать с той же конфигурацией.
Пример параллельного запуска двух версий PHP (TS и NTS) с разными сокетами PHP-FPM:
# Конфигурация пула для NTS /etc/php/8.2/fpm/pool.d/www.conf
listen = /run/php/php8.2-fpm.sock
# Для TS /etc/php/8.2-zts/fpm/pool.d/www.conf
listen = /run/php/php8.2-zts-fpm.sock
Nginx на один сайт использует NTS сокет, на другой - TS, что удобно для тестирования.
Пример скрипта, который выводит все загруженные расширения и их совместимость:
<?php
$zts = PHP_ZTS ? 'TS' : 'NTS';
echo "PHP сборка: $zts\n";
echo "Загруженные расширения:\n";
foreach (get_loaded_extensions() as $ext) {
$info = new ReflectionExtension($ext);
echo " $ext (версия {$info->getVersion()})\n";
}
?>
PHP сборка: NTS Загруженные расширения: Core (версия 8.2.12) date (версия 8.2.12) ...