PHP 并发场景的 3 种解决方案-php教程

资源魔 11 0

正在秒杀,抢购等并发场景下,可能会呈现超卖的景象,正在 PHP 言语中并无原生提供并发的处理计划,因而就需求借助其余形式来完成并发管制。

列出常见的处理计划有:

应用行列步队,额定起一个过程解决行列步队,并发申请都放到行列步队中,由额定过程串行解决,并提问题就没有存正在了,然而要额定过程支持和解决提早重大,本文没有先没有探讨这类办法。

行使数据库事务特色,做原子更新,此办法需求依赖数据库的事务特点。

借助文件排他锁,正在解决下单申请的时分,用 flock 锁定一个文件,胜利拿到锁的能力解决定单。

1、行使 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/

2、行使文件排他锁 (梗阻模式)

梗阻模式下,假如过程正在猎取文件排他锁时,其它过程在占用锁的话,此过程会挂起期待其它过程开释锁后,并本人猎取到锁后,再往下执行。

示例代码:

<?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/

3、行使文件排他锁 (非梗阻模式)

非梗阻模式下,假如过程正在猎取文件排他锁时,其它过程在占用锁的话,此过程会即刻判别猎取锁失败,而且持续往下执行。\

示例代码:

<?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 并发场景的 3 种处理计划的具体内容,更多请存眷资源魔其它相干文章!

标签: php php开发教程 php开发资料 php开发自学 Redis

抱歉,评论功能暂时关闭!