* Необходимо написать класс реализующий блокировку процессов. * Блокировки используются для проверки параллельного запуска процессов * чтобы не запускать несколько одинаковых процессов тогда, когда это вредно. * * Например, скрипт импортирующий новости запускается по крону каждую минуту. * Необходимо проверить, что предыдущий импорт уже завершился, иначе * произойдёт дублирование новостей. Кроме крона могут быть и другие применения. * * Должно работать на windows и unix. * * Важно: необходимо предусмотреть ситуацию, когда скрипт импорта вылетит с FATAL * не дойдя до крайней строки освобождения блокировки. В этом случае блокировка * не должна висеть вечно. * * При выполнении этого задания, бОльшее значение имеет качество написанного вами кода. * Не важно сколько времени вы потратите: 15 минут или 1.5 часа, ведь вы пишете * универсальный класс который будет переиспользован многократно в разных проектах * для разных задач на потяжении многих лет. * * Пожалуйста, реализуйте класс и пришлите файл Blocker.php Нужна помощь в концепции, как сделать. Реализация не обязательна
Я вот думаю, реализовать через таблицу в БД, что бы получилось универсально для ОС. Выставлять к названиям файла скрипта флаги, типо: DONE, FAIL, START итд
ну у меня мыслей кроме как контроль через базу за импортом не возникает)) я никогда не работал с скриптами у которых реализована многопоточность).. поэтому и подписался))
А зачем база, если тут хватит текстового файла? Другой вопрос, что подразумевается под блокировкой? Выдать false/true на запуск или все-же запустить процесс, который будет отслеживать обращения к скриптам?
путей реализации масса, для самой рациональной нужно иметь представление о функционале PHP-FPM которая де факто становится стандартом. Всё есть в мануале. Если коротко, php позволяет осуществлять коммуникацию между мастер-процессом и дочерними процессами, запускать их от разных пользователей, даже с разными PHP.ini настройками, останавливать, вести отдельные логи и т.п. В простой реализации вашей задачи, объекты класса должны позволять делать следующее: 1. Забор очереди очередь заданий. 2. Запускать или не запускать воркеров (сколь угодно сложная логика по принятию решения о старте новых демонов в зависимости от очереди задач, свободных ресурсов и т.п.) 3. Передать воркеру задачу 4. Воркеры отрабатывают задачи из очереди и умирают. в ответе мысли правильные, поэтому нет смысла дальнейших пояснений. Хоть через что. Это всего лишь абстракция очереди задач. Что у вас там: реляционная база данных, RabbitMQ, Gearman - не важно С формулировкой мыслей у автора задания есть проблемы. Вредно - только не хотеть. Есть слип процессов, есть кил процессов, есть вред от выполнения одними и теми же процессами одной и той же задачи (операции с общими данными), есть запуск неограниченного числа демонов и забивания ими доступной памяти что при кривых настройках может означать зависание сервера. А может быть их запускать запускать когда автор задания пьян... . * При выполнении этого задания, бОльшее значение имеет качество написанного вами кода задания
это не тестовое задание а бред ебанутого архитектора --- Добавлено --- не масса. там везде рейс кондишн сплошь и рядом блокировки делать вообще сложно и желательно делать однопоточного менеджера, иначе рейс кондишн, рейс кондишн и рейс кондишн.
Ну тогда не нужно никакого списка запущенных скриптов, потому что это тоже рейскондишн. Каждый экземпляр класса должен отвечать только за запуск одного скрипта и сверять возможность его запуска по блокировке файла. Если не блокирован, то запустить, заблокировать файл и отследить скрипт (завершение работы или аварийный выход), после чего разблокировать файл. P.S.: Нельзя ли это реализовать средствами операционной системы?
Но не в случае, когда заблокировавший поток пофейлился, не успев блокировку снять. Тогда мы получаем другую проблему - deadlock. --- Добавлено --- Ну как-то так супервайзеры многопоточные и работают. Другое дело, что хз, накой это на PHP писать, е-мое? Синдром золотого молотка, чесслово.
рейс кондишн порождает не список, а способ работы с ним лочка файла имеет один плюс - не нужно запрашивать, блокирован ли файл. Именно при запросах и возникает рейс кондишн. Если ты пошёл, спросил, запомнил, что свободно и сунулся - а там уже занято, то это рейс кондишн. Лочка файла позволяет обойти это, т.к. ты сразу лочишь файл, и только если обломился - тогда не делаешь ничего. Он либо удачно залочился, либо уже залочен кем-то другим. Если ты будешь сначала проверять, а потом лочить, то привет рейс кондишн в тот же миг. А лочка файла это операция, которая менеджерится ОСью и она типа не допускает такого. --- Добавлено --- по факту это грязный хак, который позволяет заюзать безрейсовый менеджер ОСи, предназначенный для других целей в многопоточных языках есть специальные штуки, чтобы так не надо было делать. Но вот у нас пока нет.
Здесь протестировали, что linux разблокирует файл при завершении процесса в любой ситуации, даже kill -9 https://stackoverflow.com/questions...ock-in-case-of-errors?answertab=votes#tab-top
PHP: <?php /** * Блокератор */ class Blocker { const EXT_LOCK_FILE = '.pid'; private $id; private $tmpDirectory; private $filePath; private $lock; private $fileManger; /** * @param string $id Идентификатор блокировки * @param FileManagerInterface $fileManager Файл Менеджер */ public function __construct($id, FileManagerInterface $fileManager) { $this->id = $id; $this->tmpDirectory = sys_get_temp_dir(); $this->filePath = $this->tmpDirectory . DIRECTORY_SEPARATOR . $this->id . self::EXT_LOCK_FILE; $this->fileManger = new FileManager(); } public function isLock() { $this->lock = $this->isFileAndProcessActive(); return $this->lock; } public function getLock() { return $this->lock; } /** * @return bool */ public function setLock() { if ($this->isLock()) { return true; } try { $pid = getmypid(); /** * @var File $file */ $file = $this->fileManger->create($this->filePath, 'a'); $file->truncate(0); $file->write($pid); $file->lock(LOCK_EX + LOCK_NB); $file->close(); } catch (Exception $e) { echo 'Выброшено исключение: ', $e->getMessage(), "\n"; exit(-1); } } /** * Пытается получить блокировку * * @return boolean */ public function open() { if ($this->isLock()) { return false; } $this->setLock(); return true; } /** * Высвобождает блокировку * * @return void */ public function close() { /** * @var File $file */ $file = $this->fileManger->create($this->filePath, 'r+'); $file->flush(); $file->lock(LOCK_UN); $file->close(); $this->fileManger->delete($file); } private function isFileAndProcessActive() { /** * @var File $file */ $file = $this->fileManger->create($this->filePath, 'r+'); if ($file->isExist()) { $pid = intval($file->getContent()); if (posix_kill($pid, 0)) { return true; } else { if (!$this->fileManger->delete($file)) { exit(-1); } } } return false; } } Набросал код, Теперь проблема с posix_kill на WIndows, как на windows проверить процесс с индефикатором? getmypid - работает как под linux как под windows