PHP IPC — Межпроцессное взаимодействие в PHP

от автора

Целью данной заметки является ознакомление PHP-разработчиков с возможностями межпроцессного взаимодействия в данном языке. Заметка не предполагает во всех деталях рассказать о каждой из возможностей, деталях реализации или показать рабочие примеры кода.

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

Так же, будет задета тема многопоточности в PHP, причём, именно потоков (Thread), так как до недавнего времени, PHP позволял (удобно) реализовать какое либо распараллеливание лишь благодаря Fork (не будем касаться извращений типа curl_multi_*, popen\fsockopen, Apache MPM и т.п.). Тема будет рассмотрена лишь в контексте IPC, поиск деталей реализации того или иного подхода оставляю читателям.

Повествование будет вестись в разрезе ПО, работающего на одном компьютере, и IPC в рамках одной ЭВМ. Это связано с тем, что межпроцессное взаимодействие в распределённых системах — это весьма и весьма обширная тема. Потому, не будут рассматриваться всяческие брокеры сообщений, базы данных, Pub\Sub, и прочие “межкомпьютерные” подходы, к тому же, они освещены на других ресурсах в Сети.

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

What does IPC look like?

Итак, что же такое межпроцессное взаимодействие?

Межпроцессное взаимодействие — (англ. Inter-Process Communication, IPC) — набор способов обмена данными между множеством потоков в одном или более процессах. Процессы могут быть запущены на одном или более компьютерах, связанных между собой сетью. IPC-способы делятся на методы обмена сообщениями, синхронизации, разделяемой памяти и удаленных вызовов (RPC). Методы IPC зависят от пропускной способности и задержки взаимодействия между потоками и типа передаваемых данных.

Describe like IPC looks like!

План конспекта следующий:

0. PCNTL
1. Sockets
2. Shared Memory
3. Semaphore, Shared Memory and IPC
4. pthreads

0. PCNTL

Расширение реализует самый базовый функционал по работе с процессами, но нас интересует работа с сигналами UNIX, а конкретнее — функция pcntl_signal, которая позволяет установить обработчик сигналов. Этот подход самый малофункциональный из всех рассматриваемых, так как он не позволяет передавать данные. С помощью этого расширения можно, например, организовать старт\стоп воркеров, или считывание задач из буфера (файл, БД, память, etc.), или сигнализацию одной части системы другой о каком то событии.

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

1. Sockets

Сокеты — (англ. socket — разъём) — название программного интерфейса для обеспечения обмена данными между процессами. Процессы при таком обмене могут исполняться как на одной ЭВМ, так и на различных ЭВМ, связанных между собой сетью. Сокет — абстрактный объект, представляющий конечную точку соединения.

Следует различать клиентские и серверные сокеты. Клиентские сокеты грубо можно сравнить с оконечными аппаратами телефонной сети, а серверные — с коммутаторами. Клиентское приложение (например, браузер) использует только клиентские сокеты, а серверное (например, веб-сервер, которому браузер посылает запросы) — как клиентские, так и серверные сокеты.

Пожалуй, это самый очевидный и наиболее известный способ реализации IPC, однако, и самый трудозатратный. Первый вариант — это создать брокер (сокет-сервер), клиентами-потоками коннектиться к нему. Тут вас ждёт увлекательный мир отладки неблокирующего ввода\вывода (а как вы хотели — писать блокирующий код?), а так же реализация многих тривиальных вещей типа обёрток над функциями расширения. Второй вариант проще, его можно применять для более простых реализаций: create_socket_pair, которая создаёт пару связанных сокетов, пример доступен по ссылке.

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

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

2. Shared Memory

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

Вариантов использования множество: и общее пространство, и выделение блоков персонально под каждый поток\процесс, обработка данных так же упрощается ввиду чёткого определения размера блока. К недостаткам можно отнести некоторую сложность в удобной реализации такого взаимодействия: придётся пробрасывать адреса блоков в дочерние процессы (в виде параметров, при запуске pcntl_fork, с помощью файлов-маркеров etc.)

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

3. Semaphore, Shared Memory and IPC

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

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

Обмен сообщениями является более «высокоуровевым» и удобным решением, чем разделяемая память, но данная тема в контексте PHP плохо освещена. К тому же, мне известны случаи, когда данная технология проявляла некоторые странности, скажем так, в своей работе, потому, следует внимательно проверять и перепроверять результаты работы кода.

4. Pthreads

И вот мы достигли segfault’а вершины эволюции как IPC, так и многопоточности в PHP.

Классный дядька по имени Joe Watkins написал расширение pthread, которое добавляет в PHP поддержку самой настоящей многопоточности. Буквально на днях (8.09.2013) вышла первая stable-версия (0.0.45), однако, автор в своём посте на Reddit весьма подробно раскрыл тему beta\stable релизов, потому, не заостряйте на этом внимание. Настоятельно рекомендую изучить всего его комментарии в теме, там много полезной информации о pthread.

В чём же достоинства? Во всём. Pthreads предоставляет крайне простой и удобный API для реализации любых ваших многопоточных фантазий. Тут вам и synchronize как в джаве, и события, и IPC с пробросом объктов! С ними, правда, не всё так гладко (см. примеры на гитхабе), и автор пишет, что эта проблема — не его рук дело, однако, с ресурсами сокетов ему всё же удалось сотрворить чудо, и теперь, результаты socket_accept из главного потока можно всунуть в дочерний — поразительно! Достаточно разобрать примеры, чтобы понять, насколько всё просто и элегантно сделано.

Описывать все возможности и прелести этого расширения не буду, всё есть на гитхабе автора и в документации на php.net
Судя по всему, автор довольно интенсивно работает над своим проектом, потому, в будущем у него может появиться ещё много интересных возможностей, stay tuned.

Для запуска расширения необходимо собрать PHP в Thread-safe режиме, вот небольшой скрипт, который сделает всё за вас:

При необходимости доработать напильником

mkdir /opt/php-ts && \ cd /opt/php-ts && \ wget http://www.php.net/get/php-5.5.3.tar.bz2/from/ua1.php.net/mirror -O php-5.5.3.tar.bz2 && \ tar -xf php-5.5.3.tar.bz2 && \ cd php-5.5.3/ext && \ git clone https://github.com/krakjoe/pthreads.git && \ cd ../ && \ ./buildconf --force && \ ./configure --disable-all --enable-pthreads --enable-maintainer-zts && \ make && \ TEST_PHP_EXECUTABLE=sapi/cli/php sapi/cli/php run-tests.php ext/pthreads  && \ alias php-ts="/opt/php-ts/php-5.5.3/sapi/cli/php" 

Does he look like a Pipe?

На этом, пожалуй, всё. Хотя язык и ограничен в своих возможностях IPC, тем не менее, он позволяет писать эффективные приложения с использованием различных подходов для реализации межпроцессного взаимодействия. Тем из вас, перед кем сейчас стоит задача реализации такого взаимодействия, рекомендую внимательно изучить все перечисленные в заметке способы, так как они не взаимозаменяемы, но эффективно дополняют друг друга.

P.S. Непосредственно к теме статьи это не относится, зато очень даже применимо к некоторым описанным здесь моментам, а именно, блокировании IO и несовершенстве событийной модели: рекомендую ознакомиться с расширениями Eio и Ev (автор обеих osmanov).

ссылка на оригинал статьи http://habrahabr.ru/post/193270/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *