php скрипт — перенаправление ввода в другой экземпляр

#php #io #pipe #flock

#php #io #канал #flock

Вопрос:

У меня есть php-скрипт, который используется flock() для запрета нескольких экземпляров, если скрипт уже запущен.

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

ie :

test.php :

 #!/usr/bin/env php
<?php

$lock_file = fopen ( getcwd () . '/'. basename(__FILE__, '.php') . '.pid', 'c' );
$got_lock = flock ( $lock_file, LOCK_EX | LOCK_NB, $wouldblock );
if ($lock_file === false || (! $got_lock amp;amp; ! $wouldblock)) :
    throw new Exception ( "Error opening or locking lock file" );
    error_log("execption thrown");
elseif (! $got_lock amp;amp; $wouldblock) :
    exit ( "Another instance is already running; terminating.n" );
endif;

while (true) :
    $input = $argv; // or incomming datas from other script ?
    unset($argv);
    if (is_array($input)) :
        foreach ($input as $a) :
            echo $a;
        endforeach;
    else :
        echo $input;
    endif;
endwhile;

?>
  

Теперь, если я запущу :

php -f test.php arg1 arg2

php -f test.php arg3 arg4

Второй вызов также завершается, но я бы хотел, чтобы arg3 и arg4 передавались в основной процесс.

Как я могу это сделать?

Ответ №1:

другими словами, вам нужен способ связи с процессом, который уже существует? он же IPC, есть много способов сделать это. самый быстрый способ — это разделяемая память. но использование базы данных или сокетов unix было бы проще реализовать. вот пример использования SQLite:

всякий раз, когда это удобно для обработки сообщений, делайте это:

 while(NULL!==($message=check_for_message())){//handle all messages
echo "got a mesage!:";
var_dump($message);
}
  

и функция «check_for_message»:

 //returns string("message") if there is a message available.
// else returns NULL
//Warning, do not try to optimize this function with prepared statements, unless you know what you're doing, in SQLIte they will lock the database from writing.
function check_for_message(){
static $db=false;
if($db===false){
$db=new PDO('sqlite:ipc.db3','','',array(PDO::ATTR_EMULATE_PREPARES => false,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$db->exec(
'
CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, message TEXT);

'
);
register_shutdown_function(function()use(amp;$db){
$db=NULL;
unset($db);
unlink("ipc.db3");
});
}
$message=$db->query("SELECT id,message FROM messages LIMIT 1",PDO::FETCH_ASSOC);

foreach($message as $ret){
$db->query("DELETE FROM messages WHERE id = ".$db->quote($ret['id']));
return $ret['message'];
}
return NULL;
}
  

и для отправки сообщения:

пример использования:

 foreach($argv as $arg){
sendmessage($arg);
}
  

функция:

 function sendmessage(string $message){
$db=new PDO('sqlite:ipc.db3','','',array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
));
$db->query('INSERT INTO messages (message) VALUES('.$db->quote($message).');');
}
  

примечания: я думаю, вы могли бы оптимизировать эту функцию, используя sqlite в режиме WAL и подготовленные операторы. я думаю, вы могли бы оптимизировать его еще больше, поместив его в общую память с помощью PDO ::ATTR_PERSISTENT , но afaik, это халтурно с использованием недокументированных функций, и я бы не ожидал, что это сработает для таких вещей, как HHVM PHP. в unix (* BSD, Mac OS X, Linux и т. Д.) Использование сокета unix в любом случае будет быстрее. было бы еще быстрее использовать необработанную общую память, но реализация очередей сообщений в общей памяти несколько сложнее..
вы также можете изучить возможность установки обработчика сигналов, например, SIGUSR1, чтобы указать, что сообщение ожидает, см. http://php.net/manual/en/function.pcntl-signal.php

Комментарии:

1. Сначала я посмотрю IPC, я этого не знал, в противном случае подход sql кажется хорошим решением, однако, если он не вызывает слишком много проблем с одновременным доступом. Спасибо!

2. @Khorwin вот почему я использовал SQLite, скажем, обычный текстовый файл или что-то в этом роде. механизм SQLite заботится о синхронизации между практически бесконечным числом процессов, которые хотят одновременно записывать / считывать из него / удалять из него 🙂 никаких условий гонки, никаких случайных перезаписей, все необходимые блокировки выполняются SQLite.