Widodws下面加锁失败, Linux下是ok的
在修改用户余额/积分的时候需要增加一个业务锁来实现具体的功能
需要加锁的时候发现加锁失败,然后去追了一下插件的源码,发现代码核心用的都是Symfony/lock,然后去翻看官方文件, 跳过插件直接去加锁
平台 | 第1秒发送第一次请求 | 第1次程序响应时间 | 第2秒发送第二次请求 | 第2次程序响应时间 | 结果 |
---|---|---|---|---|---|
Webman | 加锁成功,业务执行中 | 5s | 加锁成功,业务执行中 | 10s | ✗ |
Thinkphp6 | 加锁成功,业务执行中 | 5s | 加锁失败,返回异常 | 0s | ✓ |
https://www.workerman.net/plugin/55
https://www.workerman.net/plugin/56
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';
}
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;
}
"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"
},
感觉是你加锁成功并完成业务以后,没有释放锁造成。
tp
框架因为fpm的原因在请求结束自动给你释放了,Webman
却不会。推测Webman显示一次
Webman加锁成功
,以后每次都是gg
.tp
框架取决于httpSend
阻塞时间,如果真的是阻塞5s。那每5秒内显示一次TP6加锁成功
然后显示gg
,按周期循环。createLock 这个第三个参数就是是否自动释放锁, 默认是true
TP 同时请求2次, 第一次等待5秒后显示TP加锁成功, 第二次无需等待秒返回gg
Webman 同时请求2次, 一直显示的是Webman加锁成功, 不会出现gg的情况
真实的业务场景TP肯定是对的, 第一次请求加锁成功->程序执行中, 此时用户来了第二次请求->加锁失败->立马返回异常
也就是说Webman的那两个插件其实都是有问题的
$this->httpSend("http://tp6.com/index.php/pipe/curl");
后面自己释放锁。
$lock->release();
他的自动释放肯定有条件的,比如业务执行超过一定时间。你知道业务阻塞5s,那超时就要设置比这长。
程序设置的是超时30秒自动释放锁或者业务执行完毕后自动释放,
还有httpSend的实现方式,两个地方的都是一样的吗?比如同步异步之类的
httpSend一样的, 代码全部都是一模一样,
我更新了那个测试的结果, 你看下
感觉你还是把手动释放的代码加上再测试
$lock->release();
。你把
httpSend
变成sleep(5)
试试看。取消了
httpSend
增加了
结果还是和之前一样, 第二次请求是10秒钟后执行成功
那我也搞不懂了
你是不是在windows上面测试的,默认只有一个线程处理请求。
是在windows上测试的
我在windows上测试了,感觉像是请求被阻塞了。
windows 测试,就一个进程,处理完了一个在处理下一个,本身就是有序的,怎么并发请求?你的用linux测试
有点坑啊,我还用webman搞了个正式运营的网站。
官方文档说不建议在windows下使用,我理解为windows的webman性能不如linux的webman。
按现在的情况,感觉window下的webman干不过fpm框架的,毕竟apache、nginx都支持windows的。
现在看来在windows下的性能的确不行
// 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"
在Linux上测试没有问题,需要注意的是谷歌浏览器多页签对同一url串行访问阻塞问题
破案了, 这个windows下面的确不行, 必须得linux环境下面才可以
第三个参数改一样。
进程数量调多些
破案了, 这个windows下面的确不行, 必须得linux环境下面这个锁才可以
windows版本下的workman不支持多线程,所以一个阻塞就会阻塞所有的请求。windows下面千万不要用于正式环境。
作者开发的另外一个版本,估计是已经放弃了:
https://github.com/walkor/workerman-MT
感谢, 我测试用的是windows, 生产用的是linux
测试了一下,自定义进程可以解决阻塞问题。
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下面调试,需要使用两个浏览器进行测试,否则会阻塞,看不出效果。