>  기사  >  백엔드 개발  >  PHP 동시성 시나리오에 대한 여러 솔루션

PHP 동시성 시나리오에 대한 여러 솔루션

步履不停
步履不停원래의
2019-07-01 15:30:333445검색

PHP 동시성 시나리오에 대한 여러 솔루션

플래시 세일, 급매 등의 동시 시나리오에서는 과매도가 발생할 수 있습니다. 따라서 PHP 언어에는 기본 동시성 솔루션이 없습니다. 동시성 제어를 달성하려면 필요합니다.

나열된 일반적인 솔루션은 다음과 같습니다.

  1. 큐를 사용하고, 큐를 처리하기 위해 추가 프로세스를 시작하고, 모든 동시 요청을 queue. 추가 프로세스에 의해 순차적으로 처리되는 경우 동시성 문제는 존재하지 않지만 추가 프로세스 지원과 심각한 처리 지연이 필요합니다. 이 기사에서는 이 방법에 대해 먼저 설명하지 않습니다.
  2. 데이터베이스의 트랜잭션 특성을 사용하여 원자성 업데이트를 수행합니다. 이 방법은 데이터베이스의 트랜잭션 특성에 의존합니다.
  3. 파일 단독 잠금 기능을 사용하면 주문 요청 처리 시 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 test 사용

$ 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 튜토리얼

컬럼을 방문하여 알아보세요!

위 내용은 PHP 동시성 시나리오에 대한 여러 솔루션의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.