Symfony/lock业务锁插件无法在Webman里加锁

qq7467466

已解决

Widodws下面加锁失败, Linux下是ok的


使用场景

在修改用户余额/积分的时候需要增加一个业务锁来实现具体的功能

问题描述

需要加锁的时候发现加锁失败,然后去追了一下插件的源码,发现代码核心用的都是Symfony/lock,然后去翻看官方文件, 跳过插件直接去加锁


测试结果(只测试了Redis锁) (Windows环境)

平台 第1秒发送第一次请求 第1次程序响应时间 第2秒发送第二次请求 第2次程序响应时间 结果
Webman 加锁成功,业务执行中 5s 加锁成功,业务执行中 10s
Thinkphp6 加锁成功,业务执行中 5s 加锁失败,返回异常 0s

相关插件

https://www.workerman.net/plugin/55
https://www.workerman.net/plugin/56

Webman程序代码

    use support\Redis;
    use Symfony\Component\Lock\LockFactory;
    use Symfony\Component\Lock\Store\RedisStore;

    public function lock(){
        $redis = Redis::connection('default')->client();
        $store = new RedisStore($redis,300);
        $factory = new LockFactory($store);
        $lock = $factory->createLock('pdf-invoice-generation-webman');

        if ($lock->acquire()) {
            //模拟接口请求5秒后响应
            $this->httpSend("http://tp6.com/index.php/pipe/curl");
            return "Webman加锁成功";
        }
        return 'gg';
    }

Thinkphp 6程序代码

    use Symfony\Component\Lock\LockFactory;
    use Symfony\Component\Lock\Store\RedisStore;

    public function lock(){
        $redis = new \Redis();
        $redis->connect('127.0.0.1', '6379');
        $redis->auth('admin123');
        $store = new RedisStore($redis, 300, false);
        $factory = new LockFactory($store);
        $lock = $factory->createLock('pdf-invoice-generation-tp6');

        if ($lock->acquire()) {
            //模拟接口请求5秒后响应
            $this->httpSend('http://tp6.com/index.php/pipe/curl');
            echo 'TP6加锁成功';die;
        }
        echo 'gg';die;
    }

操作系统环境及workerman/webman等具体版本

  "require": {
    "php": ">=8.0",
    "workerman/webman-framework": "^1.5.0",
    "monolog/monolog": "^2.0",
    "yzh52521/webman-lock": "^1.0",
    "psr/container": "^1.1.1",
    "illuminate/redis": "^9.52",
    "illuminate/events": "^9.52",
    "guzzlehttp/guzzle": "^7.5"
  },
1338 6 0
6个回答

ichynul

感觉是你加锁成功并完成业务以后,没有释放锁造成。
tp框架因为fpm的原因在请求结束自动给你释放了,Webman却不会。
推测Webman显示一次Webman加锁成功,以后每次都是gg.
tp框架取决于httpSend阻塞时间,如果真的是阻塞5s。那每5秒内显示一次TP6加锁成功然后显示gg,按周期循环。

  • qq7467466 2023-03-15

    createLock 这个第三个参数就是是否自动释放锁, 默认是true

    TP 同时请求2次, 第一次等待5秒后显示TP加锁成功, 第二次无需等待秒返回gg
    Webman 同时请求2次, 一直显示的是Webman加锁成功, 不会出现gg的情况

  • qq7467466 2023-03-15

    真实的业务场景TP肯定是对的, 第一次请求加锁成功->程序执行中, 此时用户来了第二次请求->加锁失败->立马返回异常

    也就是说Webman的那两个插件其实都是有问题的

  • ichynul 2023-03-15

    $this->httpSend("http://tp6.com/index.php/pipe/curl");
    后面自己释放锁。$lock->release();
    他的自动释放肯定有条件的,比如业务执行超过一定时间。你知道业务阻塞5s,那超时就要设置比这长。

  • qq7467466 2023-03-15

    程序设置的是超时30秒自动释放锁或者业务执行完毕后自动释放,

  • ichynul 2023-03-15

    还有httpSend的实现方式,两个地方的都是一样的吗?比如同步异步之类的

  • qq7467466 2023-03-15

    httpSend一样的, 代码全部都是一模一样,

  • qq7467466 2023-03-15

    我更新了那个测试的结果, 你看下

  • ichynul 2023-03-15

    感觉你还是把手动释放的代码加上再测试$lock->release();

  • ichynul 2023-03-15

    你把httpSend变成sleep(5)试试看。

  • qq7467466 2023-03-15

    取消了httpSend
    增加了

    sleep(5);
    $lock->release();

    结果还是和之前一样, 第二次请求是10秒钟后执行成功

  • ichynul 2023-03-15

    那我也搞不懂了

  • ichynul 2023-03-15

    你是不是在windows上面测试的,默认只有一个线程处理请求。

  • qq7467466 2023-03-15

    是在windows上测试的

  • linfly 2023-03-15

    我在windows上测试了,感觉像是请求被阻塞了。

  • tanhongbin 2023-03-16

    windows 测试,就一个进程,处理完了一个在处理下一个,本身就是有序的,怎么并发请求?你的用linux测试

  • ichynul 2023-03-16

    有点坑啊,我还用webman搞了个正式运营的网站。
    官方文档说不建议在windows下使用,我理解为windows的webman性能不如linux的webman。
    按现在的情况,感觉window下的webman干不过fpm框架的,毕竟apache、nginx都支持windows的。

  • qq7467466 2023-03-16

    现在看来在windows下的性能的确不行

linfly
public function lock()
{
    var_dump('start ' . microtime(true));
    $redis = Redis::connection()->client();
    $store = new RedisStore($redis, 300);
    $factory = new LockFactory($store);
    var_dump('lock ' . microtime(true));
    $lock = $factory->createLock('pdf-invoice-generation-webman');
    if ($lock->acquire()) {
        //模拟接口请求5秒后响应
        sleep(5);
        $lock->release();
        var_dump('lock release ' . microtime(true));
        return "Webman加锁成功";
    }
    return 'gg';
}

// windows环境
// 'count' => cpu_count() * 4,

// 两个请求同时访问
string(20) "start 1678857468.215"
string(20) "lock 1678857468.2428"
string(28) "lock release 1678857473.2614"
string(21) "start 1678857473.2629"
string(20) "lock 1678857473.2632"
string(28) "lock release 1678857478.2766"

  • linfly 2023-03-15

    在Linux上测试没有问题,需要注意的是谷歌浏览器多页签对同一url串行访问阻塞问题

  • qq7467466 2023-03-15

    破案了, 这个windows下面的确不行, 必须得linux环境下面才可以

ichynul



第三个参数改一样。

进程数量调多些

  • qq7467466 2023-03-15

    破案了, 这个windows下面的确不行, 必须得linux环境下面这个锁才可以

ichynul

windows版本下的workman不支持多线程,所以一个阻塞就会阻塞所有的请求。windows下面千万不要用于正式环境。
作者开发的另外一个版本,估计是已经放弃了:
https://github.com/walkor/workerman-MT

  • qq7467466 2023-03-15

    感谢, 我测试用的是windows, 生产用的是linux

ichynul

测试了一下,自定义进程可以解决阻塞问题。
https://www.workerman.net/doc/webman/others/task.html
感觉windows环境可以再优化一下。
webman http://0.0.0.0:8787 4 [ok]
上面的进程数数4无意义,只会误导人。
应该按照设置的进程数量,自动拆分,每个端口对应一个进程,端口范围8787~8791。至于怎么负载均衡,由用户自己去使用apache或nginx处理。
webman http://0.0.0.0:8787-8791 4 [ok]

  • 暂无评论
深蓝

windows下面调试,需要使用两个浏览器进行测试,否则会阻塞,看不出效果。

  • 暂无评论
年代过于久远,无法发表回答
🔝