为我的Symfony应用程序中的所有命令实现锁定



我遵循以下指南:https://symfony.com/doc/current/console/lockable_trait.html并为我的一个命令实现了命令锁特性,看看它是如何工作的。它像描述的那样工作,然后我将在我的所有命令中实现它。但问题是我有大约50个命令和:

  • 我不想花时间为每个命令添加必要的代码
  • 我想集中管理命令锁定。我的意思是,在常规命令中添加额外的选项,以便我未来的管理中心使用它们。现在,我需要一个非常简单的常规命令选项protected function isLocked(),这将帮助我管理命令是否应该具有可锁定功能。

因此,我进入SymfonyComponentConsoleCommandLockableTrait的源,并在一段时间后为事件console.command创建以下侦听器:

use SymfonyComponentConsoleEventConsoleCommandEvent;
use SymfonyComponentConsoleExceptionLogicException;
use SymfonyComponentLockLock;
use SymfonyComponentLockLockFactory;
use SymfonyComponentLockLockInterface;
use SymfonyComponentLockStoreFlockStore;
use SymfonyComponentLockStoreSemaphoreStore;
class LockCommandsListener
{
/**
* @var array<string, Lock>
*/
private $commandLocks = [];
private static function init()
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
}
}
public function onConsoleCommand(ConsoleCommandEvent $event)
{
static::init();
$name = $event->getCommand()->getName();
$this->ensureLockNotPlaced($name);
$lock = $this->createLock($name);
$this->commandLocks[$name] = $lock;
if (!$lock->acquire()) {
$this->disableCommand($event, $name);
}
}
private function disableCommand(ConsoleCommandEvent $event, string $name)
{
unset($this->commandLocks[$name]);
$event->getOutput()->writeln('The command ' . $name . ' is already running');
$event->disableCommand();
$event->getCommand()->setCode()
}
private function createLock(string $name): LockInterface
{
if (SemaphoreStore::isSupported()) {
$store = new SemaphoreStore();
} else {
$store = new FlockStore();
}
return (new LockFactory($store))->createLock($name);
}
private function ensureLockNotPlaced(string $name)
{
if (isset($this->commandLocks[$name])) {
throw new LogicException('A lock is already in place.');
}
}
}

我做了一些测试,它有点工作。但我不确定这是正确的做法。

另一个问题是,当我禁用一个命令时,我找不到适当的退出代码。我应该禁用它吗?但退出码似乎是一个很好的功能。特别是当涉及到这个侦听器测试(PHPUnit测试)时。

还有测试本身。如何在测试类中并行运行命令?现在我有这个:

class LockCommandTest extends CommandTest
{
public function testOneCommandCanBeRun()
{
$commandTester = new ApplicationTester($this->application);
$commandTester->run([
'command' => 'app:dummy-command'
]);
$output = $commandTester->getDisplay();
dd($output);
}
}

它将只允许一个一个地运行我的命令。但是我想同时运行它们,所以在运行第一个之后,第二个将失败(带有一些退出代码)。

对于我来说,使后台任务的最好方法是通过主管,创建配置文件,如:

[program:your_service]
command=/usr/local/bin/php /srv/www/bin/console <your:app:command>
priority=1
numprocs=1
# Each 5 min.
startsecs=300
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)02d
user=root

这是确保命令只在一个进程中运行的最好方法

最新更新