【问题标题】:Symfony Lock Component does not lock – how to fix?Symfony 锁定组件不锁定——如何解决?
【发布时间】:2018-05-26 05:35:55
【问题描述】:

我最近升级到 Symfony 3.4.x,由于弃用警告而重构 LockHandler 并陷入奇怪的行为。

重构前的命令代码:

class FooCommand
{
    protected function configure() { /* ... does not matter ... */ }
    protected function lock() : bool
    {
        $resource = $this->getName();
        $lock     = new \Symfony\Component\Filesystem\LockHandler($resource);

        return $lock->lock();
    }
    protected function execute()
    {
        if (!$this->lock()) return 0;

        // Execute some task
    }
}

它可以防止同时运行两个命令——第二个只是完成而不做工作。这很好。

但是在建议重构之后,它允许同时运行许多命令。这是失败的。如何防止执行?新代码:

class FooCommand
{
    protected function configure() { /* ... does not matter ... */ }
    protected function lock() : bool
    {
        $resource = $this->getName();
        $store    = new \Symfony\Component\Lock\FlockStore(sys_get_temp_dir());
        $factory  = new \Symfony\Component\Lock\Factory($store);
        $lock     = $factory->createLock($resource);

        return $lock->acquire();
    }
    protected function execute()
    {
        if (!$this->lock()) return 0;

        // Execute some task
    }
}

注意 #1:我不关心很多服务器,只关心一个应用程序实例。

注意 #2:如果进程被终止,则必须解锁并运行新命令。

【问题讨论】:

    标签: php symfony locking symfony-3.4


    【解决方案1】:

    您必须使用 LockableTrait 特征

        use Symfony\Component\Console\Command\LockableTrait;
        use Symfony\Component\Console\Command\Command
    
        class FooCommand  extends Command
        {
            use LockableTrait;
    .....
    protected function execute(InputInterface $input, OutputInterface $output)
        {
            if (!$this->lock()) {
                $output->writeln('The command is already running in another process.');
    
                return 0;
            }
    // If you prefer to wait until the lock is released, use this:
            // $this->lock(null, true);
    
            // ...
    
            // if not released explicitly, Symfony releases the lock
            // automatically when the execution of the command ends
            $this->release();
    
    }
    

    【讨论】:

    • 谢谢!效果很好。遗憾的是文档不包含此内容。
    • 需要注意的是,LockableTrait 实际上只是 Symfony 控制台命令的助手。它只实现了两个锁定存储,它们都是本地的。它们是FlockStoreSemaphoreStore 商店。这不适用于MemcachedStoreRedisStore。如果需要后者中的任何一个商店,那么LockableTrait 将无济于事。
    • 感谢您注意到这一点!我看到了特质的来源——它真的很简单。就我而言,本地存储就足够了。
    【解决方案2】:

    除了 Mohamed 的回答之外,为命令储物柜分配一个 id 很重要。

    否则锁会出现与其他命令的并发问题,特别是如果您有不同的环境(测试、生产、阶段......)。您会看到该命令没有按预期的周期执行。

    这个 id 可以在 lock() 语句本身上分配。

        use Symfony\Component\Console\Command\LockableTrait;
        use Symfony\Component\Console\Command\Command
    
        class FooCommand  extends Command
        {
           use LockableTrait;
           .....
           protected function execute(InputInterface $input, OutputInterface $output)
           {
             if (!$this->lock('FooCommand'.getenv('APP_ENV'))) {
                $output->writeln('The command is already running in another process.');
                return 0;
             }
    
             $this->release();
           }
        }
    

    【讨论】:

      猜你喜欢
      • 2018-06-14
      • 1970-01-01
      • 2010-10-16
      • 2019-01-04
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2012-01-16
      相关资源
      最近更新 更多