百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>网页制作> 分享几种PHP并发场景的解决方案
分享文章到:

分享几种PHP并发场景的解决方案

发布时间:09/01 来源:未知 浏览: 关键词:

在秒杀,抢购等并发场景下,大概会显现超卖的现象,在 PHP 说话中并没有原生供给并发的解决方案,因此就需要借助其他方式来实现并发操纵。

列出常见的解决方案有:

使用队列,额外起一个进程处置队列,并发恳求都放到队列中,由额外进程串行处置,并发问题就不存在了,但是要额外进程支撑乃至处置延迟严峻,本文不先不计议这种办法。

利用数据库事务特点,做原子更新,此办法需要依靠数据库的事务特性。

借助文件排他锁,在处置下单恳求的时候,用 flock 锁定一个文件,成功拿到锁的才能处置订单。

一、利用 Redis 事务特点

redis 事务是原子操纵,可以包管订单处置的历程中数据没有被其它并发的进程修改。

示例代码:

<?php
$http = new swoole_http_server("0.0.0.0", 9509);   // 监听 9509
$http->set(array(
 'reactor_num' => 2,  //reactor thread num
 'worker_num' => 4    //worker process num
));
$http->on('request', function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid('uid-', TRUE);    // 模拟独一会员ID
 $redis = new Redis();
 $redis->connect('127.0.0.1', 6379);    // 连接 redis
 $redis->watch('rest_count');  // 监测 rest_count 可否被其它的进程更换
 $rest_count = intval($redis->get("rest_count"));  // 模拟独一订单ID
 if ($rest_count > 0){
 $value = "{$rest_count}-{$uniqid}";  // 表示当前订单,被当前会员抢到了
 // do something ... 主如果模拟会员抢到单后大概要停止的一些密集运算
 $rand = rand(100, 1000000);
 $sum = 0;
 for ($i = 0; $i < $rand; $i++) {$sum += $i;}
 // redis 事务
 $redis->multi();
 $redis->lPush('uniqids', $value);
 $redis->decr('rest_count');
 $replies = $redis->exec();  // 施行以上 redis 事务
 // 假如 rest_count 的值被其它的并发进程更换了,以上事务将回滚
 if (!$replies) {
 echo "订单 {$value} 回滚" . PHP_EOL;
 }
 }
 $redis->unwatch();
});
$http->start();

使用 ab 测试

$ ab -t 20 -c 10 http://192.168.1.104:9509/

二、利用文件排他锁 (堵塞模式)

堵塞模式下,假如进程在猎取文件排他锁时,其它进程正在占用锁的话,此进程会挂起等候其它进程开释锁后,并本人猎取到锁后,再往下施行。

示例代码:

<?php
$http = new swoole_http_server("0.0.0.0", 9510);
$http->set(array(
 'reactor_num' => 2,  //reactor thread num
 'worker_num' => 4    //worker process num
));
$http->on('request', function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid('uid-', TRUE);
 $redis = new Redis();
 $redis->connect('127.0.0.1', 6379);
 $fp = fopen("lock.txt", "w+");
 // 堵塞(等候)模式, 要取得独占锁定(写入的程序)
 if (flock($fp,LOCK_EX)) {  //锁定当前指针
 // 成功取得锁后,安心处置订单
 $rest_count = intval($redis->get("rest_count"));
 $value = "{$rest_count}-{$uniqid}";
 if ($rest_count > 0) {
 // do something ...
 $rand = rand(100, 1000000);
 $sum = 0;
 for ($i = 0; $i < $rand; $i++) {$sum += $i;}
 $redis->lPush('uniqids', $value);
 $redis->decr('rest_count');
 }
 // 订单处置完成后,再开释锁
 flock($fp, LOCK_UN);
 }
 fclose($fp);
});
$http->start();

使用 ab 测试

$ ab -t 20 -c 10 http://192.168.1.104:9510/

三、利用文件排他锁 (非堵塞模式)

非堵塞模式下,假如进程在猎取文件排他锁时,其它进程正在占用锁的话,此进程会立刻推断猎取锁失败,并且连续往下施行。\

示例代码:

<?php
$http = new swoole_http_server("0.0.0.0", 9511);
$http->set(array(
 'reactor_num' => 2,  //reactor thread num
 'worker_num' => 4    //worker process num
));
$http->on('request', function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid('uid-', TRUE);
 $redis = new Redis();
 $redis->connect('127.0.0.1', 6379);
 $fp = fopen("lock.txt", "w+");
 // 非堵塞模式, 假如不但愿 flock() 在锁按时堵塞,则给 lock 加上 LOCK_NB
 if(flock($fp,LOCK_EX | LOCK_NB))   //锁定当前指针
 {
 // 成功取得锁后,安心处置订单
 $rest_count = intval($redis->get("rest_count"));
 $value = "{$rest_count}-{$uniqid}";
 if($rest_count > 0){
 // do something ...
 $rand = rand(100, 1000000);
 $sum=0;
 for ($i=0;$i<$rand;$i++){ $sum+=$i; }
 $redis->lPush('uniqids', $value);
 $redis->decr('rest_count');
 }
 // 订单处置完成后,再开释锁
 flock($fp,LOCK_UN);
 } else {
 // 假如猎取锁失败,立刻进入这里施行
 echo "{$uniqid} - 系统忙碌,请稍后再试".PHP_EOL;
 }
 fclose($fp);
});
$http->start();

使用 ab 测试

$ ab -t 20 -c 10 http://192.168.1.104:9511/

最后给出三种处置方式的测试结果比力

redis 事务方式:

......
Concurrency Level:      10
Time taken for tests:   20.005 seconds
Complete requests:      17537
Failed requests:        0
Total transferred:      2578380 bytes
HTML transferred:       0 bytes
Requests per second:    876.62 [#/sec] (mean)
Time per request:       11.407 [ms] (mean)
Time per request:       1.141 [ms] (mean, across all concurrent requests)
Transfer rate:          125.86 [Kbytes/sec] received
......

文件排他锁(堵塞模式):

......
Concurrency Level:      10
Time taken for tests:   20.003 seconds
Complete requests:      8205
Failed requests:        0
Total transferred:      1206282 bytes
HTML transferred:       0 bytes
Requests per second:    410.19 [#/sec] (mean)
Time per request:       24.379 [ms] (mean)
Time per request:       2.438 [ms] (mean, across all concurrent requests)
Transfer rate:          58.89 [Kbytes/sec] received
......

文件排他锁(非堵塞模式):

......
Concurrency Level:      10
Time taken for tests:   20.002 seconds
Complete requests:      8616
Failed requests:        0
Total transferred:      1266846 bytes
HTML transferred:       0 bytes
Requests per second:    430.77 [#/sec] (mean)
Time per request:       23.214 [ms] (mean)
Time per request:       2.321 [ms] (mean, across all concurrent requests)
Transfer rate:          61.85 [Kbytes/sec] received
......

经测试结果对照,redis 事务方式优于文件排他锁方式,而文件排他锁方式中,非堵塞模式优于堵塞模式。

相关教程引荐:《PHP教程》

以上就是分享几种PHP并发场景的解决方案的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有150人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板