Максимальное количество потоков php

Реализация многопоточного сервера на PHP

Данная публикация не претендует на полноту решения поставленного вопроса. Сервер разрабатывается исключительно в ознакомительных целях. Многие важные вопросы, такие как, например, обработка ошибок сокетов, опущены. Для реализации многопоточного сервера мы будем использовать, конечно же, потоки. Очень часто приходится видеть фразу, что, мол, в PHP потоков нет. Так вот это неправда. Потоки есть, но реализованы в отдельном расширении pthreads.

Для начала нам понадобится сборка PHP, скомпилированная с флагом thread safety. Я использую Windows для работы, поэтому скачал готовый пакет здесь. Нужно лишь правильно выбрать разрядность ОС, нужную версию PHP и, конечно же, Thread Safe версию. На протяжении статьи будет предполагаться, что архив с PHP мы распаковали в C:\php директорию. Далее нам нужно установить расширение pthreads. Идем сюда и выбираем версию, соответствующую скачанной версии PHP и разрядности системы. Из архива копируем файл php_pthreads.dll в директорию C:\php\ext и файл pthreadVC2.dll в директории C:\php и C:\Windows\System32. В директории C:\php переименовываем файл php.ini-development в php.ini и добавляем в него такую строку:

Также находим и раскоменчиваем директиву extension_dir и выставляем ей значение «C:\php\ext» (у меня в версии PHP7 относительные пути не заработали). Открываем командную строку и проверяем:

В конце первой строки вывода мы должны увидеть пометку (ZTS). Переходим непосредственно к реализации сервера. Создаем файл (в моём случае он будет располагаться по адресу C:\server.php. Для начала создадим сокет, который будет слушать порт 8080 на нашей локальной машине.

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($server, '127.0.0.1', 8080); socket_listen($server);

Далее создаём пул воркеров.

$pool = new Pool(10, Worker::class);

Первый аргумент устанавливает максимальное количество действующих потоков, второй имя класса воркера. Для каких либо более определенных задач можно описать свой класс, унаследовав его от класса Worker. Мы же будем использовать оригинальный класс. Забегая вперед скажу, что в классе потока установленный воркер можно получить через $this->worker.

Читайте также:  Примитивы синхронизации python asyncio

Далее реализуем класс, который будет выполняться в отдельном потоке. Класс должен наследоваться от Threaded.

class Task extends Threaded < protected $socket; public function __construct($socket) < $this->socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); // при попытке закрытия сокета я получаю ошибку zend_mm_heap currupted, поэтому эту часть в тестовом решении опускаю //socket_close($this->socket); > > > 

Наш класс принимает в конструкторе сокет соединения с клиентом. Так же действия, выполняемые в потоке, должны быть описаны в методе run(). В моем случае это ответ клиенту базовых заголовков и текста «Hello world!».

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

 $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > > 

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

 register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); 

Собственно всё. Запускаем сервер в командной строке и пробуем открыть в браузере localhost:8080.

cd C:\ C:\php\php.exe server.php

Ниже привожу полный код сервера.

 socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); > > > register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > >

Источник

Maximum php-fpm threads per one core

I would like to ask how to get the best (maximum) number of php-fpm threads per one core? How to benchmark it (on linux/debian)? And how to recognize that this count is already too much? Thank you.

1 Answer 1

You probably mean processes in the pool. This depends primarily on how much blocking does your actual PHP code. The more it just waits, the more processes you need to have, because more children will just block and wait for something (for example — for external HTTP requests to end). This actual number does depend on the amount of memory you have, on the other hand. The basic rule 2-4 processes per core applies to some ideal code that doesn’t block much. Of course you can determine the formal number of threads per core by looking at the specs of your CPU, for instance # of Threads per core for Intel E5-2640 CPU would be 2.

The simple approach on determining the actual number of children in the pool is simple: make it #of Threads for your CPU at the start, then increase slightly, while repeating the benchmarking test for your installation. Once the performance stops to increase, or once you hit the memory cap, stop increasing.

Also keep in mind that in a situation when some external resource will hit it’s cap or become unresponsive (i.e. — database server, or some external HTTP server that your code may query) — you will hit the cap no matter how much children in the pool you have, they will just stuck in a waiting state. So you should keep in mind this possible bottleneck while determining your pool cap.

Источник

Реализация многопоточного сервера на PHP

Данная публикация не претендует на полноту решения поставленного вопроса. Сервер разрабатывается исключительно в ознакомительных целях. Многие важные вопросы, такие как, например, обработка ошибок сокетов, опущены. Для реализации многопоточного сервера мы будем использовать, конечно же, потоки. Очень часто приходится видеть фразу, что, мол, в PHP потоков нет. Так вот это неправда. Потоки есть, но реализованы в отдельном расширении pthreads.

Для начала нам понадобится сборка PHP, скомпилированная с флагом thread safety. Я использую Windows для работы, поэтому скачал готовый пакет здесь. Нужно лишь правильно выбрать разрядность ОС, нужную версию PHP и, конечно же, Thread Safe версию. На протяжении статьи будет предполагаться, что архив с PHP мы распаковали в C:\php директорию. Далее нам нужно установить расширение pthreads. Идем сюда и выбираем версию, соответствующую скачанной версии PHP и разрядности системы. Из архива копируем файл php_pthreads.dll в директорию C:\php\ext и файл pthreadVC2.dll в директории C:\php и C:\Windows\System32. В директории C:\php переименовываем файл php.ini-development в php.ini и добавляем в него такую строку:

Также находим и раскоменчиваем директиву extension_dir и выставляем ей значение «C:\php\ext» (у меня в версии PHP7 относительные пути не заработали). Открываем командную строку и проверяем:

В конце первой строки вывода мы должны увидеть пометку (ZTS). Переходим непосредственно к реализации сервера. Создаем файл (в моём случае он будет располагаться по адресу C:\server.php. Для начала создадим сокет, который будет слушать порт 8080 на нашей локальной машине.

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($server, '127.0.0.1', 8080); socket_listen($server);

Далее создаём пул воркеров.

$pool = new Pool(10, Worker::class);

Первый аргумент устанавливает максимальное количество действующих потоков, второй имя класса воркера. Для каких либо более определенных задач можно описать свой класс, унаследовав его от класса Worker. Мы же будем использовать оригинальный класс. Забегая вперед скажу, что в классе потока установленный воркер можно получить через $this->worker.

Далее реализуем класс, который будет выполняться в отдельном потоке. Класс должен наследоваться от Threaded.

class Task extends Threaded < protected $socket; public function __construct($socket) < $this->socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); // при попытке закрытия сокета я получаю ошибку zend_mm_heap currupted, поэтому эту часть в тестовом решении опускаю //socket_close($this->socket); > > > 

Наш класс принимает в конструкторе сокет соединения с клиентом. Так же действия, выполняемые в потоке, должны быть описаны в методе run(). В моем случае это ответ клиенту базовых заголовков и текста «Hello world!».

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

 $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > > 

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

 register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); 

Собственно всё. Запускаем сервер в командной строке и пробуем открыть в браузере localhost:8080.

cd C:\ C:\php\php.exe server.php

Ниже привожу полный код сервера.

 socket = $socket; > public function run() < if (!empty($this->socket)) < $response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 12\r\n\r\nHello world!"; socket_write($this->socket, $response, strlen($response)); > > > register_shutdown_function(function () use ($server, $pool) < if (!empty($server)) < socket_close($server); >$pool->shutdown(); >); $servers = [$server]; while (true) < $read = $servers; if (socket_select($read, $write, $except, 0) >= 0 && in_array($server, $read)) < $task = new Task(socket_accept($server)); $pool->submit($task); > >

Источник

Русские Блоги

php7.1 конфигурация нескольких экземпляров PHP-FPM и соответствующая конфигурация nginx

Эта статья в основном посвящена настройке нескольких экземпляров php-fpm через php7.1 и настройке балансировки нагрузки нескольких экземпляров с помощью nginx.

1. Сервер балансировки нагрузки (сервер) просматривает файл конфигурации nginx.

Вы можете видеть, что пользователь использует nginx здесь

2. Просмотрите файл конфигурации php-fpm на сервере приложений (в Интернете).

Обнаружил, что пользователь в php-fpm является apache

Изменить user = nginx Поскольку на сервере приложений нет пользователя nginx, запуск не выполняется.

3. Добавьте пользователя nginx на веб-сервер;

4. Файл конфигурации веб-сервера php-fpm www.conf изменяется следующим образом:

1. Измените user = apache на nginx

Во-вторых, измените адрес порта прослушивания на: listen = 0.0.0.0:9000

3. Добавьте адрес allow_clients: listen.allowed_clients = 127.0.0.1, IP-адрес сервера

5. Измените максимальное количество потоков php-fpm (поскольку объем памяти веб-сервера составляет 8 ГБ, поэтому я открыл здесь до трех php-fpm)

С практической точки зрения, один экземпляр может открыть до 200-300 процессов, а более 500 легко допустить ошибки.
Объем памяти, занимаемый каждым процессом, составляет около 5-15 млн. Из расчета 10 млн. 500 процессов имеют только 5 ГБ, а теперь многие высокопроизводительные машины имеют 64 ГБ или даже 128 ГБ памяти. Экземпляр не может полностью использовать ресурсы памяти.

pm.max_children = 256
6. Измените количество потоков, открытых при запуске

Пока что php-fpm в основном настроен.

6. Скопируйте файл конфигурации php-fpm www.conf www1.conf, www2.conf и измените порт прослушивания на: 9001, 9002.

7. Запустите несколько экземпляров.

php-fpm —config-php /etc/php-fpm.d/www.conf

php-fpm —config-php /etc/php-fpm.d/www1.conf

php-fpm —config-php /etc/php-fpm.d/www2.conf

Посмотрите, что запущено несколько процессов php-fpm. Многоэкземпляр Php-fpm успешно запущен.

Добавьте файл конфигурации myserver.conf в /etc/nginx/conf.d следующим образом:

upstream myfastcgi < сервер 192.168.1.10:9000 weight = 1; #fpm instance server 192.168.1.10:9001 weight=1; server 192.168.1.10:9002 weight=1; >server < listen 80 ; root указывает на абсолютный адрес каталога папок сервера машины экземпляра fpm без косой черты в конце папки; # указывает на каталог папки сервера машины экземпляра fpm # Запретить сканирование гусеничных инструментов, таких как Scrapy if ($http_user_agent ~* "Scrapy|Sogou web spider|Baiduspider") < return 403; ># Запретить доступ для указания UA, а UA пусто #if ($http_user_agent ~ "FeedDemon|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|YisouSpider|HttpClient|MJ12bot|heritrix|EasouSpider|LinkpadBot|Ezooms|bingbot|NET|CLR|^$" ) # < # return 403; #># Запретить сканирование без GET | HEAD | POST if ($request_method !~ ^(GET|HEAD|POST)$) < return 403; ># Добавить некоторый доступ к специальному user_agent if ($http_user_agent ~ "Mozilla/4.0\ \(compatible;\ MSIE\ 6.0;\ Windows\ NT\ 5.1;\ SV1;\ .NET\ CLR\ 1.1.4322;\ .NET\ CLR\ 2.0.50727\)") < return 404; ># Статическая конфигурация файла location /ev < # autoindex on; #Настроить, отображать ли каталог истекает 1d; # файл кэша клиента на 1 день псевдоним статический адрес файла, без косой черты в конце, адрес сервера; >location / < proxy_buffer_size 1M; proxy_buffers 12 1M; proxy_busy_buffers_size 6M; proxy_temp_file_write_size 6M; proxy_set_header Host $ host; # Если вы не хотите изменять значение заголовка запроса «Host», вы можете установить его следующим образом: proxy_set_header Host $ http_host; однако, если заголовок клиента не содержит этот заголовок, запрос передается на внутренний сервер Без этой головы. В этом случае лучше использовать переменную $ host, ее значением является значение поля «Host», когда запрос содержит заголовок запроса «Host», и значение виртуального хоста, когда запрос не содержит заголовок запроса «Host». Основной домен proxy_set_header X-Real-IP $ remote_addr; # Отправить реальный ip клиента на внутренний веб-сервер proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; # Отправка IP-адреса реального клиента на внутренний веб-сервер index index.php; try_files $uri $uri/ /index.php?$query_string; >location ~ \.php$ < proxy_buffer_size 1M; proxy_buffers 12 1M; proxy_busy_buffers_size 6M; proxy_temp_file_write_size 6M; proxy_set_header Host $ host; # Если вы не хотите изменять значение заголовка запроса «Host», вы можете установить его следующим образом: proxy_set_header Host $ http_host; однако, если заголовок клиента не содержит этот заголовок, запрос передается на внутренний сервер Без этой головы. В этом случае лучше использовать переменную $ host, ее значением является значение поля «Host», когда запрос содержит заголовок запроса «Host», и значение виртуального хоста, когда запрос не содержит заголовок запроса «Host». Основной домен proxy_set_header X-Real-IP $ remote_addr; # Отправить реальный ip клиента на внутренний веб-сервер proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; # Отправка IP-адреса реального клиента на внутренний веб-сервер fastcgi_pass myfastcgi; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; ># Deny access to .htaccess files error_log /var/log/nginx/site_error.log; access_log /var/log/nginx/site_access.log; > 

После завершения настройки запустите nginx: systemctl запустите nginx.

Источник

Оцените статью