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

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

青灯夜游
青灯夜游앞으로
2020-07-21 17:15:452350검색

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

깜박 세일, 급박 세일과 같은 동시 시나리오에서는 과잉 판매가 발생할 수 있습니다. PHP 언어에는 기본 동시성 솔루션이 없으므로 동시성 제어를 달성하려면 다른 방법이 필요합니다.

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

큐를 사용하고, 큐를 처리할 추가 프로세스를 설정하고, 모든 동시 요청을 큐에 넣고, 추가 프로세스에 의해 순차적으로 처리합니다. 그러나 추가 프로세스가 필요하며 지원 및 처리 지연이 심각하므로 이 문서에서는 이 방법에 대해 먼저 설명하지 않습니다.

원자성 업데이트를 수행하려면 데이터베이스의 트랜잭션 특성을 사용하세요. 이 방법은 데이터베이스의 트랜잭션 특성에 의존해야 합니다.

파일 단독 잠금 기능을 이용하면 주문 요청 처리 시 Flock을 사용하여 파일 잠금에 성공한 사람만 주문을 처리할 수 있습니다.

1. Redis 트랜잭션 기능 활용

redis 트랜잭션은 원자성 작업이므로 주문 처리 중에 다른 동시 프로세스에 의해 데이터가 수정되지 않도록 할 수 있습니다.

샘플 코드:

<?php
$http = new swoole_http_server("0.0.0.0", 9509);   // 监听 9509
$http->set(array(
 &#39;reactor_num&#39; => 2,  //reactor thread num
 &#39;worker_num&#39; => 4    //worker process num
));
$http->on(&#39;request&#39;, function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid(&#39;uid-&#39;, TRUE);    // 模拟唯一用户ID
 $redis = new Redis();
 $redis->connect(&#39;127.0.0.1&#39;, 6379);    // 连接 redis
 $redis->watch(&#39;rest_count&#39;);  // 监测 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(&#39;uniqids&#39;, $value);
 $redis->decr(&#39;rest_count&#39;);
 $replies = $redis->exec();  // 执行以上 redis 事务
 // 如果 rest_count 的值被其它的并发进程更改了,以上事务将回滚
 if (!$replies) {
 echo "订单 {$value} 回滚" . PHP_EOL;
 }
 }
 $redis->unwatch();
});
$http->start();

ab test

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

2. 파일 배타적 잠금 사용(차단 모드)

차단 모드에서 해당 프로세스가 파일 배타적 잠금을 획득 중이고 다른 프로세스가 잠금을 점유하고 있는 경우 this 프로세스는 정지되어 다른 프로세스가 잠금을 해제할 때까지 기다린 다음 잠금 자체를 획득한 후 실행을 계속합니다.

샘플 코드:

<?php
$http = new swoole_http_server("0.0.0.0", 9510);
$http->set(array(
 &#39;reactor_num&#39; => 2,  //reactor thread num
 &#39;worker_num&#39; => 4    //worker process num
));
$http->on(&#39;request&#39;, function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid(&#39;uid-&#39;, TRUE);
 $redis = new Redis();
 $redis->connect(&#39;127.0.0.1&#39;, 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(&#39;uniqids&#39;, $value);
 $redis->decr(&#39;rest_count&#39;);
 }
 // 订单处理完成后,再释放锁
 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(
 &#39;reactor_num&#39; => 2,  //reactor thread num
 &#39;worker_num&#39; => 4    //worker process num
));
$http->on(&#39;request&#39;, function (swoole_http_request $request, swoole_http_response $response) {
 $uniqid = uniqid(&#39;uid-&#39;, TRUE);
 $redis = new Redis();
 $redis->connect(&#39;127.0.0.1&#39;, 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(&#39;uniqids&#39;, $value);
 $redis->decr(&#39;rest_count&#39;);
 }
 // 订单处理完成后,再释放锁
 flock($fp,LOCK_UN);
 } else {
 // 如果获取锁失败,马上进入这里执行
 echo "{$uniqid} - 系统繁忙,请稍后再试".PHP_EOL;
 }
 fclose($fp);
});
$http->start();

ab test 사용

$ 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 cnblogs.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제