webman有redis分布式锁(原子锁)功能么?

zeus

各位老哥问下,webman有没有类似laravel-cache的原子锁功能?
文档: https://learnku.com/docs/laravel/6.x/cache/5160#atomic-locks

自问自答 :
按照webman文档cache章节,安装完扩展之后,在support下增加下面两个类, 类是复制laravel框架的, 改了一点东西, 最后有使用方法.

Lock.php

<?php
namespace support;

use Illuminate\Contracts\Cache\Lock as LockContract;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\InteractsWithTime;
use Illuminate\Support\Str;

abstract class Lock implements LockContract
{
    use InteractsWithTime;

    /**
     * The name of the lock.
     *
     * @var string
     */
    protected $name;

    /**
     * The number of seconds the lock should be maintained.
     *
     * @var int
     */
    protected $seconds;

    /**
     * The scope identifier of this lock.
     *
     * @var string
     */
    protected $owner;

    /**
     * Create a new lock instance.
     *
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @return void
     */
    public function __construct($name, $seconds, $owner = null)
    {
        if (is_null($owner)) {
            $owner = Str::random();
        }

        $this->name = $name;
        $this->owner = $owner;
        $this->seconds = $seconds;
    }

    /**
     * Attempt to acquire the lock.
     *
     * @return bool
     */
    abstract public function acquire();

    /**
     * Release the lock.
     *
     * @return bool
     */
    abstract public function release();

    /**
     * Returns the owner value written into the driver for this lock.
     *
     * @return string
     */
    abstract protected function getCurrentOwner();

    /**
     * Attempt to acquire the lock.
     *
     * @param  callable|null  $callback
     * @return mixed
     */
    public function get($callback = null)
    {
        $result = $this->acquire();

        if ($result && is_callable($callback)) {
            try {
                return $callback();
            } finally {
                $this->release();
            }
        }

        return $result;
    }

    /**
     * Attempt to acquire the lock for the given number of seconds.
     *
     * @param  int  $seconds
     * @param  callable|null  $callback
     * @return bool
     *
     * @throws \Illuminate\Contracts\Cache\LockTimeoutException
     */
    public function block($seconds, $callback = null)
    {
        $starting = $this->currentTime();

        while (! $this->acquire()) {
            usleep(250 * 1000);

            if ($this->currentTime() - $seconds >= $starting) {
                throw new LockTimeoutException;
            }
        }

        if (is_callable($callback)) {
            try {
                return $callback();
            } finally {
                $this->release();
            }
        }

        return true;
    }

    /**
     * Returns the current owner of the lock.
     *
     * @return string
     */
    public function owner()
    {
        return $this->owner;
    }

    /**
     * Determines whether this lock is allowed to release the lock in the driver.
     *
     * @return bool
     */
    protected function isOwnedByCurrentProcess()
    {
        return $this->getCurrentOwner() === $this->owner;
    }
}

RedisLock.php

<?php

namespace support;

class RedisLock extends Lock
{
    /**
     * The Redis factory implementation.
     *
     * @var \Illuminate\Redis\Connections\Connection
     */
    protected $redis;

    /**
     * Create a new lock instance.
     *
     * @param  \Illuminate\Redis\Connections\Connection  $redis
     * @param  string  $name
     * @param  int  $seconds
     * @param  string|null  $owner
     * @return void
     */
    public function __construct($redis, $name, $seconds, $owner = null)
    {
        parent::__construct($name, $seconds, $owner);

        $this->redis = $redis;
    }

    /**
     * Attempt to acquire the lock.
     *
     * @return bool
     */
    public function acquire()
    {
        if ($this->seconds > 0) {
            return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
        }

        return $this->redis->setnx($this->name, $this->owner) === 1;
    }

    /**
     * Release the lock.
     *
     * @return bool
     */
    public function release()
    {
        return (bool) $this->redis->eval(self::releaseLock(), 1, $this->name, $this->owner);
    }

    public static function releaseLock()
    {
        return <<<'LUA'
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
LUA;
    }

    /**
     * Releases this lock in disregard of ownership.
     *
     * @return void
     */
    public function forceRelease()
    {
        $this->redis->del($this->name);
    }

    /**
     * Returns the owner value written into the driver for this lock.
     *
     * @return string
     */
    protected function getCurrentOwner()
    {
        return $this->redis->get($this->name);
    }
}

最后用法如下:
indexController.php下:

    public function test(Request $request)
    {
        /**
         * @var $lock RedisLock
         */
        $lock = Container::instance()->make(RedisLock::class, [Redis::connection(), 'foo', 10]); //::lock('foo', 10);

        try {
            $lock->block(5);

            // 等待最多5秒后获取的锁...
        } catch (LockTimeoutException $e) {
            // 无法获取锁...
            throw  $e;
        } finally {
            optional($lock)->release();
        }

        return json(['code' => 1, 'msg' => 'ok']);
    }
3096 4 2
4个回答

Tinywan

没有

  • 暂无评论
zeus

老哥,可以同步到分享区域么?

  • yzh52521 2022-02-11

    好像运行不起来

  • zeus 2022-02-15

    有错误日志么?

  • yzh52521 2022-02-15

    换 tp Container. Container::getInstance()->make() 可以了 webman的这个 Container::instance()->make(使用 不 起来

  • zeus 2022-02-16

    我这边用的就是 webman的这个 Container::instance()->make,没问题呀。
    待会,我也试试TP的Container

  • yzh52521 2022-02-16

    你是不是没有用 依赖自动注入

  • yzh52521 2022-02-16

    报错信息
    DI\Definition\Exception\InvalidDefinition: Entry "support\RedisLock" cannot be resolved: Entry "Illuminate\Redis\Connections\Connection" cannot be resolved: the class is not instantiable
    Full definition:
    Object (
    class = #NOT INSTANTIABLE# Illuminate\Redis\Connections\Connection
    lazy = false
    )
    Full definition:
    Object (
    class = support\RedisLock
    lazy = false
    __construct(
    $redis = get(Illuminate\Redis\Connections\Connection)
    $name = #UNDEFINED#
    $seconds = #UNDEFINED#
    $owner = (default value) NULL
    )

  • zeus 2022-02-19

    我用了webman的Container,你用的DI扩展,不一样的。

  • yzh52521 2022-02-19

    用webman肯定要开启 依赖注入的

  • zeus 2022-02-19

    默认用的就是 Webman\Container; DI扩展可能是不兼容吧, 要用锁的话,可以用其他的类

yzh52521
  • 暂无评论
liziyu

mark

年代过于久远,无法发表回答
×
🔝