Комментарии 47
не совсем понятно как ваш метод будет работать с транзакциями, когда нужно параллельно обрабатывать несколько потоков данных и каждый в своей транзакции, если у вас только один коннект к базе в родительском процессе. Т.е. подразумевается что любой поток может упасть и откатить транзакцию без вреда для остальных
0
bcgjkmpjdfnm
+17
Блин, простите.
Так вот, использовать родительские подключения к базе нельзя. Как только любой из детей умрет, с ним умрет и коннект.
Поэтому если база нужна и в родителе и в потомках — нельзя допускать чтобы соединение было открыто на момент вызова pcntl_fork().
Так вот, использовать родительские подключения к базе нельзя. Как только любой из детей умрет, с ним умрет и коннект.
Поэтому если база нужна и в родителе и в потомках — нельзя допускать чтобы соединение было открыто на момент вызова pcntl_fork().
+1
Быть может я не совсем понимаю, но вроде же есть pconnect, который живет дольше (по крайней мере в частном, но самом частом случае СУБД — mysql).
0
Я честно признаюсь, что не до конца знаю внутренности работы с pconnect, но у меня не заработало ни так ни так. Первый запрос делается, второй возвращает ошибку. Как мне кажется, проблема в том, что у коннекта есть некий уникальный идентификатор, и даже если коннект остается открытым на уровне расширения — экземпляр обертки в PHP уничтожается, тем самым отвязывая идентификатор от коннекта. При повторной попытке обратиться с тем же идентификатором начинается печалька.
0
pconnect связан с транзакциями как опера с балетом
0
Вот именно, что нельзя юзать родительский коннект, и вы решили не побороть проблему, а обойти ее путем перекладывания всей работы с базой в родителя
>> родительский процесс должен заниматься базой и дочерние процессы делать «черную работу» и возвращать результат
т.о. получаются следующие ограничения
во-первых, потомки не могут полноценно работать с базой (транзакции и все такое)
во-вторых, и это очень важно, перекладывая работу с базой на один поток, мы лишаемся всех преимуществ многопоточной работы, ведь согласитесь, 16 потоков на 16 процах писали бы в базу гораздо быстрее чем один
в-третьих, когда все потомки отработали и передали данные в родителя для записи в базу, ему нужно все эти данные записать по-очереди, и пока он пишет, он не может запустить еще потомков, ну либо тут нужно организовывать какую-то асинхронную очередь для их обработки
в общем сплошные минусы, а решение то на самом деле элементарное — сразу после форка сбрасывать коннект и соединяться заново, а родителю перед его запросами проверять — не разорван ли его коннект и пересоединяться если нужно
>> родительский процесс должен заниматься базой и дочерние процессы делать «черную работу» и возвращать результат
т.о. получаются следующие ограничения
во-первых, потомки не могут полноценно работать с базой (транзакции и все такое)
во-вторых, и это очень важно, перекладывая работу с базой на один поток, мы лишаемся всех преимуществ многопоточной работы, ведь согласитесь, 16 потоков на 16 процах писали бы в базу гораздо быстрее чем один
в-третьих, когда все потомки отработали и передали данные в родителя для записи в базу, ему нужно все эти данные записать по-очереди, и пока он пишет, он не может запустить еще потомков, ну либо тут нужно организовывать какую-то асинхронную очередь для их обработки
в общем сплошные минусы, а решение то на самом деле элементарное — сразу после форка сбрасывать коннект и соединяться заново, а родителю перед его запросами проверять — не разорван ли его коннект и пересоединяться если нужно
0
Мне кажется вы хотели ответить Автору. Лично я вообще работаю с базой только из потомков, поскольку зачастую обращение к удаленному серверу БД является наибольшей задержкой, и распараллеливание запросов к разным серверам по форкам дает существенный выигрыш.
0
нельзя всем потокам использовать одно подключение к базе, потому что когда будет race condition по использованию одного подключения из 50 потоков база скажет «простите, но я пошла нахрен» и просто закроет подключение. уже сталкивался с таким.
лучшим решением тут будет из каждого чайлда создавать собственное подключение к базе, у родителя свое
лучшим решением тут будет из каждого чайлда создавать собственное подключение к базе, у родителя свое
0
В случае данного CliScript коннект с базой данных существовать будет даже после «смерти» детей, т.к. там вызывается
system("kill -9 {$this->pid}");
он не освобождает ресурсы.0
Из опыта скажу что mysqli разрывает соединение как у детей так и родителя, нужен реконнект.
0
Как вариант, использовать такую штуку Asynchronous Tasks
0
(тестировалось только на линукс)
Отчего же на винде не потестили? Note: This extension is not available on Windows platforms. Документация — очень интересная штука, между прочим.
Отчего же на винде не потестили? Note: This extension is not available on Windows platforms. Документация — очень интересная штука, между прочим.
0
code.google.com/p/scalr/source/browse/scalr-2/trunk/app/cron-ng/jobs/Poller.php
Посмотрите как работают наши кронджобы. Мы используем Semaphore Functions: очереди, семафоры и шаред мемори. Все настраивается, можно к примеру запустить пулл процессов и он будет висеть и обрабатывать задачи как толкьо они будут появлятся в очереди. Можно просто переодически запускать крон, он стартует воркеры, делает работу и прибивается. И много других вкусных плюшек.
Посмотрите как работают наши кронджобы. Мы используем Semaphore Functions: очереди, семафоры и шаред мемори. Все настраивается, можно к примеру запустить пулл процессов и он будет висеть и обрабатывать задачи как толкьо они будут появлятся в очереди. Можно просто переодически запускать крон, он стартует воркеры, делает работу и прибивается. И много других вкусных плюшек.
+1
У меня есть некоторые наработки в эту сторону github.com/kulikov/php-threads-manager
там мультипроцессорность реализуется тремя способами (на выбор):
1. через запуск отделных процессов через popen()
2. через tcp сокеты stream_socket_client()
3. через pcntl_fork() // не доделано
В основном я пользовался popen как самым надежным и прозрачным способом. сокеты использовлись коллегами для дебага под виндой. а форки так и не доделал.
Самый главный вопрос: как наиболее оптимально организовать обмен данными между родителем и потомком. По завершению работы в потомке складывать ответ во временный файл, а родитель из него потом все считывает? Пожалуй надо так и сделать. :)
там мультипроцессорность реализуется тремя способами (на выбор):
1. через запуск отделных процессов через popen()
2. через tcp сокеты stream_socket_client()
3. через pcntl_fork() // не доделано
В основном я пользовался popen как самым надежным и прозрачным способом. сокеты использовлись коллегами для дебага под виндой. а форки так и не доделал.
Самый главный вопрос: как наиболее оптимально организовать обмен данными между родителем и потомком. По завершению работы в потомке складывать ответ во временный файл, а родитель из него потом все считывает? Пожалуй надо так и сделать. :)
+2
я тоже делал через popen
работает стабильно
работает стабильно
0
Что-то мне кажется, что shm_* (или shmop_* — но их не юзал) должны быть более эффективны, особенно учитывая нативные реализации семафоров и очередей (на которых довольно просто реализуется, например пул соединений с СУБД).
0
graber, переделай в своем проекте комменты на английский.
По своему опыту скажу. что очень тяжело использовать чужой код с комментами на японском или испанском.
По своему опыту скажу. что очень тяжело использовать чужой код с комментами на японском или испанском.
0
Всё это проходил и из опыта вот что могу сказать:
Если подключить PECL-расширение proctitle (расширение бета, по причине отсутствия тестов, но работает исправно, проверено) то воркеры смогут задавать свои заголовки в ps или top, например. Это полезно что бы знать кто какие роли выполняет и чем занят.
Fork работает по принципу copy-on-write поэтому самое тяжелое лучше выгрузить в память сразу.
Некоторые расширения ведут себя не корректно после fork-а. Так, например, mysqli разрывает соединение не только в чаилдах, но и в родителе.
Комуникацию по коммандам удобно длеать через сигналы через расширение POSIX. Для более сложной комуникации лучше использовать pair сокеты. По pair советам можно отдавать результат из воркера в мастер по завершению воркера (как-то проще чем через семафоры/мутексы). При включении в дело libevent можно получить очень функциональный событийный демон.
Не забывайте делать setsid (posix_setsid).
Если подключить PECL-расширение proctitle (расширение бета, по причине отсутствия тестов, но работает исправно, проверено) то воркеры смогут задавать свои заголовки в ps или top, например. Это полезно что бы знать кто какие роли выполняет и чем занят.
Fork работает по принципу copy-on-write поэтому самое тяжелое лучше выгрузить в память сразу.
Некоторые расширения ведут себя не корректно после fork-а. Так, например, mysqli разрывает соединение не только в чаилдах, но и в родителе.
Комуникацию по коммандам удобно длеать через сигналы через расширение POSIX. Для более сложной комуникации лучше использовать pair сокеты. По pair советам можно отдавать результат из воркера в мастер по завершению воркера (как-то проще чем через семафоры/мутексы). При включении в дело libevent можно получить очень функциональный событийный демон.
Не забывайте делать setsid (posix_setsid).
+2
Спасибо! Это то, чего мне не хватает как раз. Сходу непонятно про session leader. Если сможете на пальцах объяснить что к чему то спасибо :)
0
Если вы про SID то setsid запускает новую сессию для вызвавшего setsid процесса причем SID равен PID-у этого процесса (этот процесс называется лидером). Таким образом процесс становится независим от чужой сессии, которая может быть кем-то прибита. Каждый последующий порожденный процесс от лидера или его потомка будет содержатся в этой сессии. В некоторых системах при падении лидера падает и сессия (но жаль что далеко не во всех). По сути, сессия позволяет связать пачку процессов: команада pkill -s SID разошлёт сигнал всем процессам в сессии. ps -Fs SID выведет процессы сессии и т.д.
+1
писали игровых демонов на php. pcntl+libevent. демон висел на порту и слушал и обрабатывал HTTP запросы. был некой прослойкой между фронтом и БД. запускалось форками. управление через SIGNAL. Libevent позволял создавать таймеры и прочую вкусноту. в процессе разработки были исправлены баги в php_libevent с Тони.
+2
Понравилось :)
case SIGTERM:
echo 'opa!';
return;
0
Автор забыл указать
что это возможно только в бэдграундовских процессах
что это возможно только в бэдграундовских процессах
0
Приветствую всех)
При всех моих тёплых чувствах к PHP, он, как мне кажется, не лучший инструмент для реализации многопоточности и мультипроцессности…
Кроме того, как тут уже говорили, при создании многопоточного приложения всегда следует контролировать доступ к общим ресурсам… Для примера в методе log (к которому насколько я понимаю обращаются «рабочие») следовало воспользоваться хотя бы функцией file_put_contents с флагами FILE_APPEND и LOCK_EX.
Ещё бросилось в глаза в методе getFormattedMessage почему-то автор предпочитает вызвать 4 раза функцию str_replace:
$message_formatted = str_replace('%time', $time, self::OUTPUT_FORMAT );
$message_formatted = str_replace('%role', $role, $message_formatted);
$message_formatted = str_replace('%pid', $this->pid, $message_formatted);
$message_formatted = str_replace('%message', $message, $message_formatted);
но ведь можно было использовать массивы в качестве параметров $search и $replace… хотя тут вообще уместна другая функция — sprintf (или её аналог vsprintf)…
Код просматривал мельком, поэтому сказал о том, что бросилось в глаза (getFormattedMessage рядом с методом log =) ).
Многопоточность и мультипроцессность — эта та область где даже «гуру» совершают ошибки… возможно стоит получше изучить сам php, прежде чем хвататься за многопоточность. А если уж берёшься за неё, то лучше хорошенько разобраться во всех механизмах прежде, чем писать на хабр.
При всех моих тёплых чувствах к PHP, он, как мне кажется, не лучший инструмент для реализации многопоточности и мультипроцессности…
Кроме того, как тут уже говорили, при создании многопоточного приложения всегда следует контролировать доступ к общим ресурсам… Для примера в методе log (к которому насколько я понимаю обращаются «рабочие») следовало воспользоваться хотя бы функцией file_put_contents с флагами FILE_APPEND и LOCK_EX.
Ещё бросилось в глаза в методе getFormattedMessage почему-то автор предпочитает вызвать 4 раза функцию str_replace:
$message_formatted = str_replace('%time', $time, self::OUTPUT_FORMAT );
$message_formatted = str_replace('%role', $role, $message_formatted);
$message_formatted = str_replace('%pid', $this->pid, $message_formatted);
$message_formatted = str_replace('%message', $message, $message_formatted);
но ведь можно было использовать массивы в качестве параметров $search и $replace… хотя тут вообще уместна другая функция — sprintf (или её аналог vsprintf)…
Код просматривал мельком, поэтому сказал о том, что бросилось в глаза (getFormattedMessage рядом с методом log =) ).
Многопоточность и мультипроцессность — эта та область где даже «гуру» совершают ошибки… возможно стоит получше изучить сам php, прежде чем хвататься за многопоточность. А если уж берёшься за неё, то лучше хорошенько разобраться во всех механизмах прежде, чем писать на хабр.
+1
Может быть удобнее будет запускать процесс с помощью system(«php /path/script.php»);?
Честно говоря, не проверял этот метод, но управляю демонами через PHP
Честно говоря, не проверял этот метод, но управляю демонами через PHP
#!/usr/bin/php
<?php
echo "________________________________________\n";
$command = "restart";
if (isset($argv[1]))
$command = trim($argv[1]);
echo "Начинаем сканировать процессы...\n";
exec("ps -A -F | grep php", $output);
$folder = dirname(__FILE__);
$php = "php";
$names = array(
"system",
"inspector",
"drivers",
"android",
);
echo "Определяем демоны multitaxi...\n";
foreach ($output as $out) {
if (preg_match_all("#.*php ".$folder."/(.*)\.php#isU", $out, $matches)) {
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
$daemon = $matches[1][0];
$matches = explode(" ", $matches[0][0]);
$pid = $matches[1];
//echo $daemon;
if (in_array($daemon, $names)) {
if ($command != "view")
exec("kill -9 ".$pid);
echo "Процесс: ".$daemon." (".$pid.")\n";
}
}
}
if ($command != "stop" && $command != "view") {
echo "Запускаем процессы...\n";
foreach ($names as $d) {
exec($php." ".$folder."/".$d.".php 1>> /dev/null 2>> /dev/null &");
echo $php." ".$folder."/".$d.".php 1>> /dev/null 2>> /dev/null & \n";
}
}
echo "Готово!\n";
echo "________________________________________\n";
?>
0
Код писался на коленке за 5 минут. За
$matches[0][0] = str_replace(" ", " ", $matches[0][0]);
стыдно :)0
> exec($php." ".$folder."/".$d.".php 1>> /dev/null 2>> /dev/null &");
данная строка порождает Zombie
Осторожно, не наплодите стадо вампиров
данная строка порождает Zombie
Осторожно, не наплодите стадо вампиров
+1
Скрипт и написан для того, что бы не запускать стадо вампиров =) Сначала он ищет все запущенные с именами, заданными в массиве, убивает их, а потом запускает новые
0
ну сказано же чуть ли не открытым текстом: что в данной строчке кода содержится содержится потенциальная опасность народить кучу зомби процессов, а он за свое… мой скрипт, мой скрипт…
Конечно, если интенсивность их порождения не большая, то в принципе нет ничего страшного, но еще раз повторяю, что данный код не корректен, потому-что порождение нового процесса в бэкграундовском режиме заканчивается потерей родителя (родитель заканчивается раньше, чем его потомок) — это и приводит к зомбированию. Чтоб этого не произошло надо выполнить команду setsid() — которая в реализации данного режима не предусмотрена.
для того, чтоб запускать РНР в бэкграунде и не было побочных эффектов, специально придуман php-forker
Конечно, если интенсивность их порождения не большая, то в принципе нет ничего страшного, но еще раз повторяю, что данный код не корректен, потому-что порождение нового процесса в бэкграундовском режиме заканчивается потерей родителя (родитель заканчивается раньше, чем его потомок) — это и приводит к зомбированию. Чтоб этого не произошло надо выполнить команду setsid() — которая в реализации данного режима не предусмотрена.
для того, чтоб запускать РНР в бэкграунде и не было побочных эффектов, специально придуман php-forker
-1
— Зачем Вы доите лошадь? Для этого есть корова.
— Мы не доим лошадь, мы ездим на ней
— Говорю же, лошадь не для того, что бы ее доить, а вы все моя лошадь, моя лошадь…
— Мы не доим лошадь, мы ездим на ней
— Говорю же, лошадь не для того, что бы ее доить, а вы все моя лошадь, моя лошадь…
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Параллельные вычисления, класс-обёртка для pcntl_fork()