Сборка PHP на Mac и Linux с примерами многопоточности и pthreads

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

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

Стандартные сборки PHP из менеджеров пакетов не поддерживают многопоточность. Что нам нужно сделать, так это перестроить PHP с флагом, который включает ZTS или Zend Thread Safety, который затем позволяет нам расширить использование наших потоков. Чтобы облегчить этот процесс сборки, мы будем использовать инструмент под названием phpbrew, предназначенный для создания нескольких версий PHP и легкого переключения между ними. Затем, только после того, как наши сборки будут завершены, мы сможем экспериментировать с pthreads.

Есть некоторые ошибки, специфичные для платформы, но мы преодолеем их с помощью простых решений.

Понятно, что у вас есть машина для разработки и готовые к производству серверы, поэтому мы рассмотрим процесс сборки phpbrew как на Mac OS, так и на CentOS. Если вы используете другой дистрибутив, я выделю ресурс для конкретных требований ОС.

Начнем с создания PHP на Mac.

phpbrew Введение

phpbrew существует с 2012 года, и его продолжают развивать по сей день - некоторые приверженные сопровождающие демонстрируют, что PHP по-прежнему является важным инструментом для многих разработчиков. Инструкции по установке и использованию phpbrew понятны и понятны. Хотя это и не обязательно, на этом этапе полезно ознакомиться с phpbrew.

Зачем использовать phpbrew вместо ванильной команды сборки?

По нескольким причинам. Phpbrew не только может создавать несколько версий PHP и позволяет нам легко переключаться между ними, но также значительно упрощает процесс сборки с использованием вариантов. Вместо того, чтобы копировать множество флагов, как в обычной команде сборки, мы можем использовать ключевые слова, которые действуют либо как один флаг, либо как группа флагов. Примером варианта является +mb, который будет строить PHP с mbstring и mbregex. Перейдите непосредственно к списку вариантов здесь, чтобы узнать, что есть в наличии.

Помимо вариантов, мы можем устанавливать расширения и настраивать наш ini-файл с помощью только одной команды. Другими словами, нам не нужно беспокоиться о поддержке ini после установки расширения - phpbrew сделает это за нас. Например, мы будем использовать следующую команду для установки pthreads в нашу недавно созданную версию PHP:

phpbrew ext install github:krakjoe/pthreads

Как видите, мы устанавливаем последнюю версию (важно!) Расширения pthreads прямо из Github и включаем ее в наш файл конфигурации с помощью этой команды. Довольно мило. Это часть поддержки установка расширений, которую предлагает phpbrew.

Их ссылка на Github на домашнюю страницу проекта находится здесь:



Использование phpbrew в Mac OS

Давайте сначала установим phpbrew. В Терминале загрузите и установите phpbrew с помощью следующих команд:

curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew
chmod +x phpbrew
sudo mv phpbrew /usr/local/bin/phpbrew

После перемещения phpbrew в локальную корзину инициализируйте сценарий bash для среды оболочки:

phpbrew init
[[ -e ~/.phpbrew/bashrc ]] && source ~/.phpbrew/bashrc

Теперь выполните следующие команды, чтобы обновить сборки PHP, доступные для phpbrew:

phpbrew known
phpbrew update

Мы будем собирать последнюю версию PHP7.2, которая на момент написания была 7.2.12. Не стесняйтесь использовать более новую версию, если у вас есть к ней доступ.

На этом этапе вы также можете запустить phpbrew variants, чтобы получить полный список доступных вариантов.

Теперь phpbrew готов к использованию - и теперь для сборки PHP нужна только одна команда. Но нам нужно сделать еще одно - установить зависимости PHP.

Mac: установка зависимостей PHP

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

На данный момент мы используем Mac, и в соответствии с Разделом Mac OS на странице требований выше, мы можем использовать HomeBrew или MacPorts для их установки.

Воспользуйтесь моим советом и используйте MacPorts. Я сначала (и с нетерпением) попытался использовать HomeBrew, но столкнулся с постоянными сбоями при связывании и ошибками сборки, решения которых не было видно. Если у вас не установлен MacPorts, скачайте последнюю версию для своей ОС (10.14 Mojave является последней на данный момент) со страницы Загрузки MacPorts. После установки выполните следующие команды:

port install curl automake autoconf icu depof:php72 depof:php72-gd mcrypt bison re2c gettext openssl

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

Mac: сборка PHP

Теперь мы готовы к сборке PHP с включенным ZTS. Для этого выполните следующую команду установки phpbrew:

phpbrew install php-7.2.12 +default -- --enable-maintainer-zts

Здесь мы используем вариант +default, который включает в себя часто используемые расширения PHP. Мы также расширяем наши варианты с помощью простых флагов, которые можно добавлять к команде с помощью -- --flag1 --flagN.

Этот процесс может занять некоторое время в зависимости от вашей машины. На MacBook 12 первого поколения, на котором я тестировал эту сборку, сборка была завершена чуть более чем за 30 минут. Журнал сборки предоставляется по мере того, как что-то происходит, и вы можете отслеживать его, давая вам обратную связь в реальном времени.

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

Mac: использование новой сборки

На этом этапе вы можете запустить phpbrew list, перечислив все ваши сборки PHP. В этом списке вы заметите php-7.2.12 - только что созданную версию.

phpbrew также предоставляет команды switch и use для переключения версии PHP в зависимости от наших сборок. Постоянно переключитесь на новую сборку следующим образом:

phpbrew use php-7.2.12

В окне Терминала, в котором вы сейчас находитесь, команда php всегда будет ссылаться на недавно созданный PHP7.2.

Mac: установка pthreads

Наша последняя задача - установить расширение pthreads. Сделайте это с помощью следующей команды:

phpbrew ext install github:krakjoe/pthreads

Давайте рассмотрим еще пару расширений, которые вы, возможно, захотите установить. Я использую Composer и MongoDB, а также Xdebug в своей среде разработки. Чтобы установить эти расширения, запустите следующее:

phpbrew ext install mongodb
phpbrew ext install xdebug stable
phpbrew app get composer

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

На этом этапе мы готовы использовать pthreads на Mac. В следующем разделе мы рассмотрим процесс установки на CentOS. Переходите к разделу pthreads, если вам не нужны дальнейшие установки!

Использование phpbrew в Cent OS7

Давайте сначала рассмотрим установку зависимостей для сборки PHP на CentOS. Установите требования со следующими командами:

sudo yum install make automake gcc gcc-c++ kernel-devel
sudo yum install php php-devel php-pear bzip2-devel yum-utils bison re2c libmcrypt-devel libpqxx-devel libxslt-devel pcre-devel libcurl-devel libgsasl-devel openldap-devel, httpd-devel

Мне также нужно было установить readline в моей системе CentOS:

yum install readline-devel

Если на вашем сервере CentOS не установлен PHP, в соответствии с требованиями phpbrew рекомендуется сначала установить его. Я предпочитаю использовать RPM для установки PHP. Сделайте это, используя следующие команды:

sudo yum -y install epel-release
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7*.rpm

Обновите файл remi PHP7.2, чтобы включить php7.2. Измените enabled=0 на enabled=1 только в блоке [remi-php7.2]:

cd /etc/yum.repos.d/
#open php7.2 repo file and change enabled=0 to enabled=1
sudo vi remi-php72.repo

Наконец, установите следующие пакеты PHP с помощью yum:

sudo yum install php php-gd php-common php-mysql php-mcrypt php-devel php-xml

CentOS: установка phpbrew

Установка phpbrew идентична установке на Mac. Для полноты здесь снова перечислены команды установки:

curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew
chmod +x phpbrew
sudo mv phpbrew /usr/local/bin/phpbrew
phpbrew init
[[ -e ~/.phpbrew/bashrc ]] && source ~/.phpbrew/bashrc
phpbrew known
phpbrew update

Стоит отметить, что если вы перезагрузите сервер, вам нужно будет повторно запустить phpbrew с помощью phpbrew init и снова изменить bashrc. Вы можете настроить демон во время загрузки, чтобы он делал это автоматически после прочтения статьи.

CentOS: сборка PHP

Теперь команда сборки немного отличается от команды Mac. В CentOS соберите PHP с помощью:

phpbrew install php-7.2.12 +default +openssl=/usr -- --enable-maintainer-zts --with-libdir=lib64

Теперь мы используем +openssl=/usr вариант и дополнительный—-with-libdir=lib64 флаг. Почему это? Потому что, в отличие от сборки Mac, нам необходимо явно указать расположение OpenSSL для сборки PHP для компиляции. В противном случае мы получим ошибку в процессе сборки о том, что библиотеки OpenSSL не найдены. Эта оплошность требует простого исправления, но проблема обсуждалась на Github здесь, если вы хотите ее изучить.

Примечание о требованиях к памяти при построении PHP на VPS: если вы используете VPS, убедитесь, что у вас есть как минимум 1 ГБ свободной памяти. Я попытался собрать PHP примерно на 400 МБ свободной памяти, которая закончилась, и, следовательно, сборка не удалась. При необходимости временно обновите ресурсы VPS для сборки PHP, а затем вернитесь обратно после завершения.

CentOS: использование новой сборки

С успешной новой сборкой мы теперь можем использовать ее так же, как процесс Mac:

phpbrew use php-7.2.12

CentOS: установка потоков

Установка расширения pthreads - и других интересных - также идентична установке Mac:

phpbrew ext install github:krakjoe/pthreads
phpbrew ext install mongodb
phpbrew ext install xdebug stable
phpbrew app get composer

После завершения этого раздела у нас теперь есть подходящая сборка PHP для многопоточности на локальной машине и в производственной среде! Последний фрагмент головоломки - использование pthreads для реализации наших новых обнаруженных возможностей многопоточности.

Использование pthreads

pthreads привносит возможности PHP для многопоточности в то, что разработчики сейчас считают само собой разумеющимся в других языках. Он обеспечивает многопоточность, совместимую с PHP на основе Posix Threads. По сути, это настоящая многопоточность.

Итак, как нам использовать pthreads? Что ж, он предоставляет класс под названием Threads, из которого мы расширились. Отсюда мы используем run() метод Thread для кодирования логики, которую мы хотим реализовать в отдельном потоке. Подумайте примерно так:

class MyProgram extends Thread {
   public function run() {
      //run my program
      echo "Woo, I am running in a new thread!";
   }
}
$program1 = new MyProgram;
$program1->start() && $program1->join();
echo 'Thread finished';

Здесь мы используем два метода pthread start() и join(), которые унаследованы от Thread.

start() не требует пояснений - он вызывает метод run() в отдельном потоке, что позволяет начать нашу параллельную обработку.

join(), с другой стороны, требует немного большего объяснения:

join() позволяет нам перенести поток в текущий контекст, который, следовательно, ожидает его завершения, прежде чем наш текущий контекст продолжит выполнение.

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

В приведенном выше фрагменте кода поток $program1 присоединен к нашему основному процессу, поэтому наша последняя инструкция echo вызывается после того, как поток завершил обработку.

Пример 1: Последовательное объединение потоков

Чтобы понять, что здесь происходит, рассмотрим следующий пример, в котором 5 потоковых заданий создаются, запускаются и объединяются по порядку. Каждый процесс потока спит в течение X секунд (X является индексом потока) перед тем, как повторить закрывающий запрос:

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

В окне терминала phpbrew запустите приведенный выше сценарий, чтобы проверить это поведение, с php multi-thread1.php.

Пример 2: одновременное выполнение потоков

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

В этом примере все 5 потоков запускаются одновременно в нашем цикле, с удаленным методом join(). Затем наше основное выполнение будет спать в течение 5 секунд, пока потоки завершают обработку в фоновом режиме. Затем мы снова попытаемся присоединиться к обсуждениям. Выполните следующий пример, чтобы изучить поведение:

Здесь стоит отметить пару интересных моментов:

  • Несмотря на то, что потоки выполняются одновременно в отдельных потоках, мы все равно получаем их эхо-запросы в Терминале.
  • Мы пытаемся присоединиться к потокам, как только они закончат выполнение, но это не удается. Вместо того, чтобы PHP выдает ошибку, выполнение просто продолжается в нашем основном процессе, как показано в последнем операторе echo.

Можно ли использовать потоки в классах пространства имен?

Да, действительно могут. Если вы, например, используете автозагрузку Composer, вы можете просто расширить свои классы с помощью extends \Thread, как в наших предыдущих примерах.

Изучение pthreads

Лучший способ изучить библиотеку pthreads - обратиться к документации php.net. Всего на момент написания pthreads состоит из 9 классов и 53 методов. В действительности вам может не понадобиться использовать большинство из них. Фактически, если вы просто хотите ускорить выполнение, выполняя параллельные задачи, вам будет достаточно методов, использованных в приведенных выше примерах. Однако всегда стоит ознакомиться с возможностями пакета для использования в будущем.

Взгляните на указатель документации pthreads, чтобы начать работу. Все перечисленные там методы идут с примерами, например Thread::getCurrentThreadId - функция идентификации, которую можно вызвать в run() методе потока для получения его идентификатора.

Класс Threaded содержит ценные методы обнаружения состояния, такие как isRunning, еще один полезный метод, позволяющий нам проверить, выполняется ли метод run() в заданное время в конкретном потоке.

Заключить

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

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

Как вы используете pthreads? Было бы здорово услышать в отзывах!