Pull to refresh

Comments 78

Если вам хочеться cтранного то тогда, лучшим решением будет использовать библиотеку ØMQ
Тов. KAndy и lexdevel: целью заметки было описать решения и подходы, а не готовые продукты (за исключением pthreds), в контексте PHP для самостоятельного велосипедостроительства. Информация о 0mq, thrift и ещё куче подобны вещей гуглится сразу же, и не зря они небыли упомянуты, равно как и rmq, phpDaemon, nanoserv, ratchet, и ещё очень много других.
Сорри, что не [совсем] по теме, но меня мучает один вопрос, на который не смог сходу найти ответ, а сюда, прозреваю, заглянут хорошо разбирающиеся в вопросе люди. Суть вот в чем: у меня есть некий инит-скрипт, который запускается при старте системы и призван запустить несколько других скриптов в демон-режиме. Я использую примерно такой код:
function bkg($executable, $args = []) {
    $childPid = pcntl_fork();
    if ($childPid == 0) {
        posix_setsid();
        fclose(STDOUT); // в манах говорится, что необходимо закрыть поток стд. вывода
        fclose(STDERR); // и ошибок
        pcntl_exec($executable, $args);
    } elseif($childPid != -1) {
        return true;
    }
    return false;
}


мне же в целях отладки надо перенаправить вывод запущенных процессов в какой-нибудь файл. Подскажите, плиз, как это можно сделать?
1) суслог
2) перенаправляй все ошибки в лог
Вариант с перенаправлением потока вывода:

function bkg(){
    $childPid = pcntl_fork();
    if ($childPid == 0) {
    } elseif($childPid != -1) {
        echo 'out to cli';
        fclose(STDOUT);
        $STDOUT = fopen("log." . getmypid() .  ".out", "wb");
        echo 'out to log.out';
   }
}

bkg();


Или можно в дочернем процессе создавать лог-файл и вместо echo\print использовать файловые функции, это более предпочтительный вариант.
Хм, насколько я понял, данный код перенаправит вывод самого инит-скрипта, а необходимо перенаправить вывод именно вызываемых (при этом, к сожалению, я не имею возможности менять код вызываемых). Сработает этот прием, если переместить его обратно в секцию if ($childPid == 0)?
Ну вы же делаете fork в инит-скрипте N раз, который «запускает» N форков. Вот, для каждого из этих форков и происходит перенаправление вывода в свой файл. В итоге, у вас получится N файлов, каждый с выводом одного форка.

Вот модифицированный код, который иллюстрирует написанное:

<?php

function bkg(){
    $childPid = pcntl_fork();
    if ($childPid == 0) {
    } elseif($childPid != -1) {
        $pid = getmypid();
        echo 'iam child, out to cli, my pid is ' . $pid . PHP_EOL;
        fclose(STDOUT);
        $STDOUT = fopen("log." . $pid .  ".out", "wb");
        echo 'out to log.out, my pid is ' . $pid . PHP_EOL;
   } 
}

bkg();
bkg();
bkg();

echo 'iam parent, out to cli, my pid is ' . getmypid() . PHP_EOL;

?>
Пардон, очепятался, так правильно:

Скрытый текст
<?php

function bkg(){
    $childPid = pcntl_fork();

    if ($childPid == 0) {
        $pid = getmypid();
        echo 'iam child, out to cli, my pid is ' . $pid . PHP_EOL;
        fclose(STDOUT);
        $STDOUT = fopen("log." . $pid .  ".out", "wb");
        echo 'iam child, out to log.' . $pid .  '.out, my pid is ' . $pid . PHP_EOL;
    } elseif($childPid != -1) {
   } 
}

bkg();
bkg();
bkg();

echo 'iam parent, out to cli, my pid is ' . getmypid() . PHP_EOL;

?>
ага, я про то же ниже :)

еще раз спасибо!
Вы во всем правы, единственное, путаете местами секции if.

pcntl_fork() возвращает 0 именно в дочернем процессе, я попробовал переместить Ваш код в нужную секцию и все заработало как надо:
function bkg($executable, $args){
    $childPid = pcntl_fork();
    if ($childPid == 0) {
        posix_setsid();
        fclose(STDOUT); // в манах говорится, что необходимо закрыть поток стд. вывода
        fclose(STDERR); // и ошибок
        $pid = getmypid();
        $STDOUT = fopen("log." . $pid .  ".out", "wb");
        echo 'out to log.out, my pid is ' . $pid . PHP_EOL;
        pcntl_exec($executable, $args);
    } elseif($childPid != -1) {
        $pid = getmypid();
        echo 'i am still parent, out to cli, my pid is ' . $pid . PHP_EOL;
    }
}

bkg('/usr/bin/php', ['/domains/manuals.dev/script/tmp-child.php']);

echo 'iam parent, out to cli, my pid is ' . getmypid() . PHP_EOL;


Спасибо большое за наводку! (Я не знал, что существует еще и некая глобальная переменная $STDOUT)
Да, я уже внёс исправления, см. мой комментарий выше. vim-проказник подложил свинью :(
Не подскажете, откуда вам стало известно про $STDOUT? В документации такого не нашел, гугл тоже в данном случае не помощник.
$STDOUT это просто перменная, можете назвать её иначе, как вам нравится
STDOUT это файловый дескриптор, который обычно открыт при запуске программы, запись в него приводит к отображению записанного на терминале, и также как и любой другой файловый дескриптор его можно закрыть функцией fclose, и если после этого открыть любой файл, то новый дескриптор будет иметь значение 1 и echo будет писать уже в него, потому что echo это по сути системный вызов write(1, ...)
О, так все проясняется. Название переменной в данном случае лишь вводит в заблуждение. Спасибо.
Сществует множество стандартных потоков, которые имеют константные номера файловых дескрипторов (как ответили ниже), потому, никакой магии нет (документация на php.net). Обратите внимание на порядок закрытия\открытия стандартных потоков, имена переменных не важны, т.е, если вы хотите закрыть все три потока, а потом переоткрыть их, то открывайте в порядке возрастания их номеров (STDIN, STDOUT, STDERR), так как PHP автоматически назначает эти потоки, и явно указать, что вы сейчас открываете возможности нет. Это не касается зарезервированных потоков-обёрток типа «php://stdout».
Вообще-то я делаю, и это рекомендуется для всех, запуск приложения в двух режимах: демон или нет: разруливается опцией -d (daemon), запускается как php my_app -d в демоническом режиме

Так вот в демоническом режиме должен быть отсоединен терминал и выводы std занулены
Для отладки запускаем в отладочном режиме… Это нормальная практика, когда приложение имеет несколько режимов работы
Тут есть такой момент, что сам вызывающий скрипт не должен быть демоном, он стартует, запускает процессы и сразу завершается. А код вызываемых процессов, я менять не могу.
тут нет ничего сложного,
я уже эти пользуюсь более пяти лет…
как правило, когда что-то отлаживаешь, то работаешь в консоли и видишь в ней всю отладку.
в этом случае, твой скрипт не делает форка, который его уводит в бэкграунд и работает функционал воркера.
как только надо либо продакшен, то ставим в запускающем скрипте опцию, которая запускает функцию demonize()
Эта функция состоит из форка, в котором завершается родительский процесс, отсоединяется терминал и гасятся стд выводы.

Если твой скрипт запускает несколько демонических процессов, то так правило — это однотипные воркеры, т.е. ты можешь отлаживаться на одном воркере. А в продакшене запустить хоть 32 (так у меня было в онлайн-игре).

Если твой скрипт запускает несколько демонов, но это разные по функциональности процессы, то что-то не то с архитектурой.

Еще раз напоминаю, что можно просто настроить вывод ошибок в суслог и при необходимости его тайлись:
tail -f /var/log/my_logs
Спс за инфо.

Дело в том, что у меня немного другой кейс. Поясню. Мне нужно запускать не воркеры, а несколько сторонних скриптов в качестве демонов. Запускающий скрипт вызывается паппетом при старте системы (я юзаю вагрант). При этом, как я уже упоминал, у меня нет возможности настроить вывод в лог в самих этих скриптах, я могу править только сам вызывающий скрипт. Что самое досадное, когда я логинюсь через SSH в систему и запускаю стартующий скрипт из консоли, то и он и запускаемые скрипты работают без проблем, а вот когда запуск идет при старте системы, сторонние скрипты слетают (возможно, какие-то сервисы вроде редиса на тот момент еще не готовы, или еще что-то подобное). Исходя из этого мне и потребовалось узнать, что они там такое пишут в STDOUT и STDERR. Просто чтобы выяснить, откуда ноги растут.
Тащем та, автор публикации уже подсказал годный для меня способ выше, но и Вам спасибо за участие!
function daemon()
{
        checkPid();
        $pid =  pcntl_fork();
 
        if ($pid < 0)
                die('error call fork()'.PHP_EOL);
 
        if ($pid)
                exit();
 
        echo 'start process pid=', posix_getpid(), PHP_EOL;
 
        $sid = posix_setsid();
        if ($sid < 0) exit;
 
        global $pidFilename;
        file_put_contents($pidFilename, posix_getpid());
 
        fclose(STDOUT);
        fclose(STDIN);
        fclose(STDERR);
}
Реализация собственной демонизации в общем-то не такая уж важная штука. Многие современные процесс-менеджеры наоборот предпочитают недемонизирующиеся сервисы. Ну и демонизацию всегда можно быстро добавить с помощью daemontools типа
start-stop-daemon --start --background --chuid www-data:www-data --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $LOGFILE || return 1
это можно использовать если нужно запустить один процесс, а если у тебя пятьдесят worker-ов?
Не совсем понял. Если нужно запустить 50 воркеров то скорее всего стоит сделать один мастер-процесс, который нафоркает 50 детей. Но у этого мастера не обязательно реализовывать ещё и демонизацию (отвязывание от консоли).
Хотя некоторые менеджеры процессов (supervisord вроде бы) умеют запускать и N экземпляров процесса.
За обзор — спасибо, вполне доступно, правда все это давно всем известно, кто проработал на РНР более пары лет, а если и нет, то хорошо гуглится… Но, чтоб это все понять, прежде чем что-либо делать, советую прочитать про основы IPC у Стивенсона, где на примерах все хорошо рассказано и описано.

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

Потому, было решено собрать всё воедино, чтобы новички какраз и знали, что есть и с чего можно начать. К сожалению, с понятными примерами тоже не всё гладко: все эти технологии в контексте PHP так или иначе описаны на php.net и других интернетах с достаточным количеством приветмиров, а что то сложное городить — так новичкам будет сложно понять всю магию, а тем к то в теме оно и не надо. Так что, я рекомендую ознакомиться со всем, это не займёт много времени, а дальше углубляться в изучение выбранного направления.
[ offttop] Брать ведро попкорна?
Ну начнем с конца, с самого интересного. Объясни мне хоть одну задачу, реализованную средствами РНР с использованием тредов и желательно с примерами?
PS — а ты в курсе, что при использовании запросов в тредах — PHP сигфолтится?
Вы башорг парсить будете в один поток, или в несколько? А если не башорг, а уютненький фейсбучек? За рафинированными примерами можете сходить на гитхаб к автору pthreads, за нерафинированными — сесть и подумать, какие задачи в вебе могут быть решены не в один поток. Задачи, кстати, могут быть не только сайтописательскими, а и носить прикладной характер, скажем, морфологическая обработка неких текстов или иных больших массивов данных, генерация статистической информации и так далее.

Другое дело, что PHP со своими форками\потоками, как известно каждому бородатому С-шнику, не для каждой задачи подходит, но ведь речь сейчас не об этом, верно? :)

И о каких запросах идёт речь?
для задач парсинга есть multi_curl, который хорошо с этим справляется, В этих задача, основное время тратится на ожидание операций ввода/вывода, так что однопоточный режим режим именно самое то!

Сколько уже писали на эту мету, что асинхронный мультеплексорный режим лучше чем синхронный многотредовый, так как создание каждого треда требует дополнительных ресурсов, где-то по 10М на тред (это только pthreads, не считая сколько потребует пхпешный враппер) + системные ресурсы на переключение контента и прочее…

И в дополнение, в многопоточном режиме все равно в БД разными потоками не положишь.
Так что для решение таких задач самое то: запустить сразу несколько процессов с мултикурлом, парсить и класть в БД,

так что, про задачи вопрос не раскрыт.
Не уповайте на задежрки IO, есть куча задач, где длительный процесс их обработки нивелирует какие либо значимые задержки (загрузить данные в память и далее с ними работать), примеры таких задач я привёл. К тому же, многопоточная хитектура часто нужна про соображениям дизайна системы: вы таки будете писать сокет-сервер в один поток\процесс? Или применение принципов map-reduce в рамках одной машины, когда памяти не хватает, чтобы обработать весь массив данных, используя, скажем, сложную сортировку или выборку по какому то параметру?

Да, и чего вы решили, что если многотредовый — то обязательно синхронный? Треды могут вообще не знать ничего о внешнем мире и соседях, делать лишь свою часть задачи, они могут быть инициализированы заранее в виде пула, как делает множество другого ПО. И про прожорливость тредов вы неправду пишете, поток pthreads жрёт ~4.6 Мб, но никак не 10.

В дополнение, в многопоточном режиме данные можно сохранять в разные таблицы, и потом их мерджить (если это одна задача), мне знаком проект, где используются даже разные БД на свой пул форков. А уж возможности БД в виде отложенной записи, или синхронизация через кеши в памяти ещё сильнее ослабляют зависимость от медленного IO.

Полагаю, что вопрос аргументации, что вопрос про задачи не раскрыт, не раскрыт.
>поток pthreads жрёт ~4.6 Мб, но никак не 10.
зависит от настроек ядра, теоретически 4.6, но на практике более 10. Не спорил бы, если бы не решал многотрдовые задачи.
более менее рабочее решение — это организация на каждый тред по своему евентлупу, но я не встречал нормальной реализации. Распределение коннекций по тнредам идет не нормально (не подчиняется нормальному закону распределения, т.е. не равномерно). Приходится городить костыли, типа считать кол-во коннекций и если больше среднего, то отпускать ассепт.
В итоге прходим к чему-то такому:
cnn[0]=1 0.39% [ 0.00]
cnn[1]=8 3.14% [ 0.03]
cnn[2]=7 2.75% [ 0.03]
cnn[3]=5 1.96% [ 0.02]
cnn[4]=7 2.75% [ 0.03]
cnn[5]=9 3.53% [ 0.04]
cnn[6]=5 1.96% [ 0.02]
cnn[7]=7 2.75% [ 0.03]
cnn[8]=7 2.75% [ 0.03]
cnn[9]=2 0.78% [ 0.01]
cnn[10]=7 2.75% [ 0.03]
cnn[11]=3 1.18% [ 0.01]
cnn[12]=7 2.75% [ 0.03]
cnn[13]=8 3.14% [ 0.03]
cnn[14]=9 3.53% [ 0.04]
cnn[15]=9 3.53% [ 0.04]
cnn[16]=9 3.53% [ 0.04]
cnn[17]=9 3.53% [ 0.04]
cnn[18]=7 2.75% [ 0.03]
cnn[19]=9 3.53% [ 0.04]
cnn[20]=9 3.53% [ 0.04]
cnn[21]=9 3.53% [ 0.04]
cnn[22]=9 3.53% [ 0.04]
cnn[23]=9 3.53% [ 0.04]
cnn[24]=9 3.53% [ 0.04]
cnn[25]=8 3.14% [ 0.03]
cnn[26]=8 3.14% [ 0.03]
cnn[27]=9 3.53% [ 0.04]
cnn[28]=5 1.96% [ 0.02]
cnn[29]=9 3.53% [ 0.04]
cnn[30]=2 0.78% [ 0.01]
cnn[31]=9 3.53% [ 0.04]
cnn[32]=7 2.75% [ 0.03]
cnn[33]=9 3.53% [ 0.04]
cnn[34]=9 3.53% [ 0.04]
— all=255 avg= 7.29

но это лучше, чем когда треть перегружена, стт=15 в вторая а треть cnn=0
Помните хороший фильм, про бочку спирта в реке и про «это у тебя в уме, а у меня в бочке»? Вот так и тут: у вас «на практике» 10, а у меня на практике 4.6, с дефолтными настройками ядра на CentOS, которая ставилась по принципу «далее->далее->далее->ОК». ЧЯДНТ?
>Да, и чего вы решили, что если многотредовый — то обязательно синхронный?
асинхронный в/в при многотредовости вообще превращается в ад

нет ни одного нормального решения асинхронный I/O при многотредовой архитектуре.
решение с передачей файлового дескриптора через внутреннюю очередь в тред, работает медленее чем асинхронный режим
как один из выходов — можно использовать коротины. Но тут опять свои танцы с бубнами…

как один из недостатков — это загруженность только одного ядра. Ну а сами танцы начинаются при работе с БД…
При работе с коротинами — есть возможность мускуль заставить работать асинхронно, но тут нужно отлавливать события асинхронности, и программирование превращается в кэллбэков ад.

короче — куда ни глянь, одни грабли…
короче — куда ни глянь, одни грабли…


«Мы выживали как могли» (С) :)
Никакого ада нет, libevent и компания вам в помощь. В конце концов, можно изобрести велосипед (даже с круглыми колёсами) и без него, но не так элегантно и *троллейбус.jpg*. Опять же, зависит от конкретной задачи, я бы не стал так однозначно утверждать, что правильно, а что нет.

[оффтопик] nodejs же как то работает, и ничего: там вам и асинхронность, и коллбек-хэлл ада нет никакого.
node.js один evenloop — он ассептит дескриптор и передает его через очередь другим тредам.
uvlib была разобрана по косточкам :)
весь ад начинается, когда из треда надо делать более чем два связанных мускульных запроса. Если интересны подходы асинхронного мускуля, можешь почитать мою статью в сентябрьском выпуске «Системного Администратора»
статья выйдет в октябре.
А в чём проблема сделать два связанных запроса? Открываем транзакцию и делаем. Или, если данные запросов куда то нужно пробрасывать, скажем, в мастер-поток, так как тред по каким то причинам этого делать не может, то можно для этого сгородить специальную структуру (буфер).
Задачи, кстати, могут быть не только сайтописательскими, а и носить прикладной характер, скажем, морфологическая обработка неких текстов или иных больших массивов данных, генерация статистической информации и так далее.


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

то же самое можно сказать и про морфологический разбор. Да и в онлайне он точно не нужен.

ну про большие наборы данных ты вообще загнул. Загнется пхп на их обсчет. Реальный случай в моей практике, обсчет биллинга: foeach просто не выдержал обхода нескольких миллионов. Пришлось делать цикл через while. Ну, это было лет пять назад, а теперь просто юзаем MapReduce

так что переходим к семафорам? расскажешь, как на практике надо использовать семафоры?
сам-то рассчитывал что либо?


Конечно же нет, я тут просто новые баззворды осваиваю.

практически все статистические расчеты есть — обработка одного большого массива данных.


Про map-reduce вы, видимо, слышали, потому, это утверждение говорит лишь о том, о чём говорит: да, большое количество данных, но ведь и обработать их можно (относительно) без проблем. И на PHP такие вещи тоже можно писать, но не подумайте, я не агитирую за велосипеды и готов даже носить футболку с лого «Hadoop». Просто задачи бывают разных масштабов.

то же самое можно сказать и про морфологический разбор. Да и в онлайне он точно не нужен.


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

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

так что переходим к семафорам? расскажешь, как на практике надо использовать семафоры?


NO.
кончилось ведро попкорна?
Офтоп: удивился не обнаружив в тегах Тарантино. :)
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Магия в неявном создании переменной, хранящей объект класса, такое нововведение некорректно обрабатывается, предполагаю, потому, что pthreads использует некоторые… неоднозначные вещи в коде, и потому, автор мог не учесть всех, нюансов zend'а. Ответ следует искать в работе менеджера памяти и сборщика мусора, ведь, в таком случае, после завершения вызова, объект уже как бы «бесхозный». К тому же, насколько мне известно, автор активно работает с версией 5.3, потому, мог не уследить за всеми тонкостями новых версий
Обзор не полон. Можно было бы привести хотя бы основы работы с сигналами(в частности с наиболее частыми сигналами вида SIGTERM, SIGCHILD e.t.c), системными сообщениями. Вы забыли такие важные вещи почему-то в контексте языка, как Gearman(кстати это наилучший способ подружить php и C), демоны очередей(~MQ), и события(их C-шные реализации кстати достаточно простые) Libevent, Libevent2, помимо упомянутых официально-неофициальное php-расширение Event сделанное по мотивам LibEV только без неймспейсов и гораздо более функциональное. И раз уж затронули тему сокетов, то можно было бы хоть написать вкратце разницу между блокирующими и неблокирующими.
Но как почва для размышлений — статья это отличное начало!
Я уже отвечал на подобные вопросы, и в заметке об этом сказано, но всё же повторюсь: целью было рассмотреть IPC лишь в контексте PHP, и не готовый софт, а сами принципы\подходы (и их реализации в языке), а то, о чём вы пишете, хоть и работает с ним, но область применения и возможности далеко не ограничиваются одним PHP. К тому же, эти решения гуглятся довольно быстро, а уж в документации и примерах недостатка нет (Кстати, о сигналах тоже вагон информации, даже на php.net).

Собственно, постигать дзен типа различий в IO и тонкостях каждого подхода, читателю следует самому, так как раскрытие таких деталей в статье потянет за собой ещё и описание решений, которые позволяют обойти те или иные ограничения (читай libev* и co), а тут уже полёт фантазии не ограничен.

О тех же 0mq и gearman`ах многие хотя бы слышали, а вот о таких, казалось бы, базовых вещах — нет. Потому, вы верно заметили — это почва для размышлений, причём, рассчитанная на более-менее опытных пользователей, которая бы как то систематизировала их представления о вариантах реализации IPC в PHP.
согл с автором, это тема отдельной большой статьи,
и эту тему уже не раз раскрывали на Хабре
Хороший обзор, хотелось бы примеров каждого из пунктов, буду ждать ещё статью :)

Почему это popen извращение? Сам popen это конечно же только односторонний IPC, но есть более продвинутый вариант proc_open. Благодаря которому получается unix-way взаимодействие между процессами через пайп.
Обе эти функции находятся в «экстеншене» core PHP, а это значит, что не нужно дополнительно собирать модули так, как это требуется для тех же pcntl и sockets, не говоря уже про кастомное расширение pthreads.
sockets тоже не нужно
Примеры, к сожалению, противоречат идеологии заметки, но, я подумаю :)

По поводу proc_open: это довольно неудобное решение (в сравнении с остальными), к тому же, одним proc_open вы не обойдётесь, придётся задействовать ещё и расширение Stream, а так же файловые функции для чтения\записи (или их аналоги из stream_*), перенаправления вывода, что при разработке чего то дальше приветмира оборачивается хаосом в коде, под всё это придётся писать обёртки и собирать в итоге под каким то удобоваримом интерфейсом. Ещё у этого решения есть проблемы с блокирующимся pipe-ами, с кодировкой что то было, на Windows (хотя это малозначимая проблема в 99% случаев) вообще можно словить цирк и феерию с ограничениями размера буферов. С popen припоминаю проблемы при chroot, при чтении бинарных данных.

Короче говоря, это работающий метод, но я бы крайне не рекомендовал его использование, в особенности, когда есть более приемлимые решения, лучше уж в файлики писать :)
На самом деле интересует реальный опыт используемого вами метода IPC.

По моему опыту мне хватило proc_open для задачи взаимодействия одного мастер процесса и десяти дочерних, все функции для этого есть в стандратном экстеншене:

$ php --re standard | grep -E 'fwrite|fgets|stream_select|proc_open'
    Function [ <internal:standard> function proc_open ] {
    Function [ <internal:standard> function fgets ] {
    Function [ <internal:standard> function fwrite ] {
    Function [ <internal:standard> function stream_select ] {


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

С файлами проблема в том, что пишущий процесс должен закрыть файл, чтобы читащий мог нормально читать данные, чтобы не нужно было отслеживать последнюю прочитанную позицию, кажется как-то так.
Я использовал все приведённые в статье способы в виде готовых, рабочих решений, кроме pthreads — с ним я только писал хеллоуВорлды и пускал скупую слезу восхищения.

сигналы — в простейших системах типа «стартуем N процессов и откуда то ими рулим», или для синхронизации;
сокеты — для сервера\клиентов очереди сообщений, велосипедостроения типа селениума, коммуникации с питон-скриптами, да много где ещё;
шаред мемори и семафоры — в проектах для работы с внешними сервисами по API, когда требовалась синхронизация по определённым критериями, так как API, так сказать, «непотокобезопастный», и не требовалось активного обмена данными между потоками и их раскидыванием по разным машинам, а так же по работе с ffmpeg;
«извращения» — в молодые зелёные годы в поисках решений по сабжу заметки :) (вероятно, информация об описанных мною проблемах могла стать неактуальной)

Где то между всем этим проскакивал и libevent.

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

Про файлики я не всерьёз, но и там можно сделать ход конём с помощью функций stream_* и ловить кайф синхронизации данных :)
расширение Stream

Потоки являются неотъемлемой частью PHP, начиная с версии 4.3.0. Для их включения не требуется каких-либо действий.


Кстати, «PCNTL» и «Semaphore, Shared Memory and IPC» под Windows не работают :( Проблема с бинарным режимом (по умолчанию винда работает с файлами в текстовом режиме) решается указанием флага «b» в "['pipe', 'rb']" (там еще несколько режимов есть, но они могут не работать в *nix)

Настоящие же проблемы в том что:
1) stream_select возвращал все потоки вместо измененного (на win 8 x64 и PHP 5.4.16 сейчас вроде нормально работает)
2) stream_set_blocking НЕ работает с proc_open, т.е. неблокирующего режима просто нет :) (а нет его потому, что неименованные пайпы, через которые это все работает, в винде этого не умеют)

Поэтому для чего то серьезного оно действительно не подходит.
Я не говорил «установить», я говорил «задействовать» расширение Stream, подразумевая лишь его использование, а не установку или что то ещё.

Да, под виндой это не работает, так как основано на чисто никсовых системах (System V IPC, etc), которые, по понятным причинам, на винду портировать несколько… затруднительно. На Windows не акцентировал внимание в заметке, так как уже давно под ней ничего не пишу, да и абсолютное большинство PHP-программистов не используют WIndows в produсtion, потому, я полагал, что всё перечисленное в заметке будет по-умолчанию применяться в *.nix и под него же адаптироваться.

Однако, оба пункта отлично работают на *nix, и серьёзные вещи с использованием этих подходов можно писать. В догонку о proc_open, цитата из доки по stream_select: «Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.»
А какой из вариантов работает без установки дополнительных расширений или пересобирания PHP?
popen/proc_open и скорее всего socket-ы.
Можно ещё обычные файлы использовать, и, если не нужен неблокирующий IO — использовать flock и co. для самописного «мьютекса».
мой мозг сломался после фразы о неблокирующем IO на flock для самописных мьютексов. Просто пока нету расширений для работы с частными случаями winAPI на php потому что это никому не нужно в рамках IPC — а с системными блокировками и там и там на самом деле все в порядке.
неблокирующем IO на flock


Я писал, что если неблокирующий IO НЕ нужен, то можно задействовать flock, да и не рассматриваю Windows, как платформу для разработки.
тем более flock на ней и не работает как заявлено.
Sign up to leave a comment.

Articles