amiralii/laravel-redlock

为 Laravel 提供的 Redis 分布式锁

4.0.2 2020-10-10 16:25 UTC

README

使用 Redis 提供通用锁定机制。实现了 Redis 提出的锁定标准。

鸣谢

此库最初由 LibiChai 在 antirez 开发的 Redlock 算法基础上构建。该库由 That's Us, Inc. 团队重构。

安装

  1. "parsadp/laravel-redlock": "^4.0.0"
  2. "composer update"
  3. Amiralii\RedLock\RedLockServiceProvider::class, 添加到 config/app.php 文件中的 providers 数组中
  4. 享受吧!

很简单!

在任何标量上设置锁。如果 lock() 方法返回 false,则未获取到锁。

存储 lock() 方法的返回结果。使用此值使用 unlock() 释放锁。

示例

此示例使用 3 秒过期时间在键 "1" 上设置锁。

如果获取了锁,则执行一些工作并释放锁。

 use Amiralii\RedLock\Facades\RedLock;

 $product_id = 1;

 $lock_token = RedLock::lock($product_id, 3000);
 
 if ($lock_token) {

     $order->submit($product_id);

     RedLock::unlock($lock_token);
 }

刷新

使用 refreshLock() 重新获取并延长锁的时间。

 use Amiralii\RedLock\Facades\RedLock;

 $product_ids = [1, 2, 3, 5, 7];

 $lock_token = RedLock::lock('order-submitter', 3000);
 
 while ($product_ids && $lock_token) {

     $order->submit(array_shift($product_ids));

     $lock_token = RedLock::refreshLock($lock_token);
 }

 RedLock::unlock($lock_token);

使用闭包更简单

使用 runLocked() 以获得更简洁的语法。该方法返回闭包的结果,如果无法获取锁,则返回 false。

 use Amiralii\RedLock\Facades\RedLock;

 $product_id = 1;

 $result = RedLock::runLocked($product_id, 3000, function () use ($order, $product_id) {
     $order->submit($product_id);
     return true;
 });

 echo $result ? 'Worked!' : 'Lock not acquired.';

使用闭包刷新

当使用闭包时,您可以轻松刷新令牌。闭包的第一个参数是 $refresh。当您想要刷新时,只需调用它。如果无法刷新锁,$refresh() 将退出闭包。

 use Amiralii\RedLock\Facades\RedLock;

 $product_ids = [1, 2, 3, 5, 7];

 $result = RedLock::runLocked($product_id, 3000, function ($refresh) use ($order, $product_ids) {
     foreach ($product_ids as $product_id) {
         $refresh();
         $order->submit($product_id);
     }
     return true;
 });

 echo $result ? 'Worked!' : 'Lock lost or never acquired.';

轻松锁定队列作业

如果您正在 Laravel 队列上运行作业,您可能希望避免同时排队相同的作业。

Amiralii\RedLock\Traits\QueueWithoutOverlap 特性通过少量修改您的作业提供了此功能。通常只需要更改两次。

  1. 作为特性使用 use Amiralii\RedLock\Traits\QueueWithoutOverlap
  2. handle() 方法更改为 handleSync()
use Amiralii\RedLock\Traits\QueueWithoutOverlap;

class OrderProductJob
{
    use QueueWithoutOverlap;

    public function __construct($order, $product_id)
    {
        $this->order = $order;
        $this->product_id = $product_id;
    }

    public function handleSync()
    {
        $this->order->submit($this->product_id);
    }

}

有时还需要指定一个 getLockKey() 方法。此方法必须返回需要锁定的字符串。

这通常是不必要的,因为锁键可以自动生成。但如果作业的数据不易转换为字符串,则必须定义 getLockKey() 方法。

此特性还提供了一个名为 refreshLock() 的刷新方法。如果 refreshLock() 无法刷新锁,则抛出异常并失败作业。

最后,您可以使用 $lock_time 属性将锁的超时时间从默认的 300 秒更改为其他值。

use Amiralii\RedLock\Traits\QueueWithoutOverlap;

class OrderProductsJob
{
    use QueueWithoutOverlap;

    protected $lock_time = 600; // 10 minutes in seconds

    public function __construct($order, array $product_ids)
    {
        $this->order = $order;
        $this->product_ids = $product_ids;
    }

    // We need to define getLockKey() because $product_ids is an array and the
    // automatic key generator can't deal with arrays.
    protected function getLockKey()
    {
        $product_ids = implode(',', $this->product_ids);
        return "OrderProductsJob:{$this->order->id}:{$product_ids}";
    }

    public function handleSync()
    {
        foreach ($this->product_ids as $product_id) {
            $this->refreshLock();
            $this->order->submit($product_id);
        }
    }

}

贡献

如果您发现错误或想为代码或文档做出贡献,您可以通过提交 问题pull request 来帮助。

许可证

MIT