Рубрики
Uncategorized

Несколько решений для параллельного сценария PHP

Автор оригинала: David Wong.

В параллельных сценариях, таких как второе убийство и захват, может быть перепроданность. На языке PHP нет собственного параллельного решения, поэтому для достижения параллельного управления необходимо использовать другие способы.

Ниже перечислены общие решения:

  • При использовании очередей для обработки очереди добавляется дополнительный процесс, и в очередь помещаются одновременные запросы. Проблема параллелизма не возникает, если дополнительный процесс обрабатывается последовательно. Однако этот метод не обсуждается в данной статье из-за дополнительной поддержки процесса и серьезной задержки обработки.
  • Используя характеристики транзакций базы данных для обновления атомов, этот метод должен зависеть от характеристик транзакций базы данных.
  • С помощью эксклюзивной блокировки файлов при обработке запроса на заказ файл блокируется с помощью flock, и заказ может быть обработан только в том случае, если блокировка успешно получена.

I. Использование Характеристик транзакций Redis

Транзакции Redis – это атомарные операции, которые гарантируют, что данные не будут изменены другими параллельными процессами во время обработки заказа.

Пример кода:

php
$http = new swoole_http_server ("0.0.0.0", 9509);//Listen 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); // Simulate unique user ID
  $redis = new Redis();
  $redis - > connect ('127.0.0.1', 6379); // connect redis

  $redis - > watch ('rest_count'); //monitor whether rest_count is changed by other processes

  $rest_count = intval ($redis - > get ("rest_count"); // Simulate unique order ID
  if($rest_count > 0){
    $value = "{rest_count} - {uniqid}"; // indicates the current order, which is grabbed by the current user

    // Do something... is mainly to simulate some intensive operations that users may have to do when they grab a single.
    $rand = rand(100, 1000000);
    $sum=0;
    for ($i=0;$i<$rand;$i++){ $sum+=$i; }

   // redis transaction
    $redis->multi();
    $redis->lPush('uniqids', $value);
    $redis->decr('rest_count');
    $replies = $redis - > exec (); // Perform the above redis transaction

   // If the rest_count value is changed by other concurrent processes, the above transaction will be rolled back
    if(!$replies){
      Echo "Order {$value} rollback". PHP_EOL;
    }
  }
  $redis->unwatch();
});

$http->start();

Использование теста AB

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

2. Использование Эксклюзивных Блокировок Файлов (Режим Блокировки)

В режиме блокировки, если другие процессы занимают блокировку, в то время как процесс получает блокировку с исключительным доступом к файлам, процесс зависнет и будет ждать, пока другие процессы снимут блокировку, и получит блокировку самостоятельно, а затем будет выполняться дальше.

Пример кода:

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+");

  // Blocking (waiting) mode, to obtain exclusive locking (written program)
  If (flock ($fp, LOCK_EX)// Lock the current pointer
  {
   // Once the lock is successful, be assured that the order will be processed.
    $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');
    }

   // Release lock after order processing is completed
    flock($fp,LOCK_UN);
  }
  fclose($fp);

});

$http->start();

Использование теста AB

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

3. Использование эксклюзивных блокировок файлов (неблокирующий режим)

В неблокирующем режиме, если другие процессы занимают блокировку, в то время как процесс получает монопольную блокировку файла, процесс немедленно определит, что блокировка получения не удалась, и продолжит выполнение.

Пример кода:

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+");

  // In non-blocking mode, add LOCK_NB to lock if you don't want flock () to block at lock time
  If (flock ($fp, LOCK_EX | LOCK_NB)// Lock the current pointer
  {
   // Once the lock is successful, be assured that the order will be processed.
    $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');
    }

   // Release lock after order processing is completed
    flock($fp,LOCK_UN);
  } else {
   // If the acquisition lock fails, enter here immediately to execute it.
    Echo "{$uniqid} - The system is busy, please try again later." 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 лучше, чем режим блокировки с исключительным доступом к файлам, а неблокирующий режим лучше, чем режим блокировки в режиме блокировки с исключительным доступом к файлам.

Вышесказанное составляет все содержание этой статьи. Я надеюсь, что это будет полезно для всех, и я надеюсь, что вы будете больше поддерживать разработчика.

Оригинал: “https://developpaper.com/several-solutions-for-php-concurrent-scenario/”