当前位置:首页 > PHP教程 > php高级应用 > 列表

详解PHP中互斥锁库hyperf-wise-locksmith的使用

发布:smiling 来源: PHP粉丝网  添加日期:2025-12-12 10:39:53 浏览: 评论:0 

在分布式系统中,如何确保多台机器之间不会产生竞争条件,是一个常见且重要的问题。hyperf-wise-locksmith 库作为 Hyperf 框架中的一员,提供了一个高效、简洁的互斥锁解决方案。

本文将带你了解这个库的安装、特性、基本与高级功能,并结合实际应用场景,展示其在项目中的应用。

hyperf-wise-locksmith 库简介

hyperf-wise-locksmith 是一个适配 Hyperf 框架的互斥锁库,它基于 pudongping/wise-locksmith 库构建。它可以帮助我们在分布式环境下进行锁的管理,确保同一时刻只有一个进程能够操作某些共享资源,从而避免数据的竞争和不一致问题。

安装

要在你的 Hyperf 项目中使用 hyperf-wise-locksmith,你需要通过 Composer 进行安装:

  1. composer require pudongping/hyperf-wise-locksmith-vvv 

确保你的环境满足以下要求:

PHP >= 8.0

hyperf ~3.0.0。

特性

hyperf-wise-locksmith 提供了多种锁机制,包括文件锁、分布式锁、红锁和协程级别的互斥锁。这些锁机制可以帮助开发者在不同的场景下保护共享资源,避免竞态条件。

基本功能

文件锁(flock)

文件锁是一种简单的锁机制,它依赖于文件系统。以下是一个使用文件锁的示例:

  1. privatefunctionflock(float $amount) 
  2.  
  3. { 
  4.  
  5. $path= BASE_PATH . '/runtime/alex.lock.cache'; 
  6.  
  7. $fileHandler= fopen($path, 'a+'); 
  8.  
  9. // fwrite($fileHandler, sprintf("%s - %s \r\n", 'Locked', microtime())); 
  10.  
  11. $res= $this->locker->flock($fileHandler, function() use($amount) { 
  12.  
  13. return$this->deductBalance($amount); 
  14.  
  15. }); 
  16.  
  17. return$res; 
  18.  
  19. } 

分布式锁(redisLock)

分布式锁适用于分布式系统,它依赖于 Redis。以下是一个使用分布式锁的示例:

  1. privatefunctionredisLock(float $amount) 
  2.  
  3. { 
  4.  
  5. $res= $this->locker->redisLock('redisLock', function() use($amount) { 
  6.  
  7. return$this->deductBalance($amount); 
  8.  
  9. }, 10); 
  10.  
  11. return$res; 
  12.  
  13. } 

高级功能

红锁(redLock)

红锁是一种更安全的分布式锁实现,它需要多个 Redis 实例。以下是一个使用红锁的示例:

  1. privatefunctionredLock(float $amount) 
  2.  
  3. { 
  4.  
  5. $res= $this->locker->redLock('redLock', function() use($amount) { 
  6.  
  7. return$this->deductBalance($amount); 
  8.  
  9. }, 10); 
  10.  
  11. return$res; 
  12.  
  13. } 

协程级别的互斥锁(channelLock)

协程级别的互斥锁适用于协程环境,它提供了一种轻量级的锁机制。以下是一个使用协程锁的示例:

  1. privatefunctionchannelLock(float $amount) 
  2.  
  3. { 
  4.  
  5. $res= $this->locker->channelLock('channelLock', function() use($amount) { 
  6.  
  7. return$this->deductBalance($amount); 
  8.  
  9. }); 
  10.  
  11. return$res; 
  12.  
  13. } 

实际应用场景

假设我们有一个在线支付系统,需要在多个请求中扣减用户的余额。如果不使用互斥锁,可能会导致超扣或扣减失败。使用 hyperf-wise-locksmith 库,我们可以确保每次扣减操作都是原子性的。

代码示例

以下是一个扣减用户余额的示例,使用了 hyperf-wise-locksmith 库:

  1. <?php 
  2.  
  3. declare(strict_types=1); 
  4.  
  5. namespaceApp\Services; 
  6.  
  7. useHyperf\Contract\StdoutLoggerInterface; 
  8.  
  9. usePudongping\HyperfWiseLocksmith\Locker; 
  10.  
  11. usePudongping\WiseLocksmith\Exception\WiseLocksmithException; 
  12.  
  13. usePudongping\WiseLocksmith\Support\Swoole\SwooleEngine; 
  14.  
  15. useThrowable; 
  16.  
  17. classAccountBalanceService 
  18.  
  19. { 
  20.  
  21. /** 
  22.  
  23. * 用户账户初始余额 
  24.  
  25. * 
  26.  
  27. * @var float|int 
  28.  
  29. */ 
  30.  
  31. privatefloat|int $balance= 10; 
  32.  
  33. publicfunction__construct( 
  34.  
  35. privateStdoutLoggerInterface $logger, 
  36.  
  37. privateLocker                $locker 
  38.  
  39. ) { 
  40.  
  41. $this->locker->setLogger($logger); 
  42.  
  43. } 
  44.  
  45. privatefunctiondeductBalance(float|int $amount) 
  46.  
  47. { 
  48.  
  49. if($this->balance >= $amount) { 
  50.  
  51. // 模拟业务处理耗时 
  52.  
  53. usleep(500 * 1000); 
  54.  
  55. $this->balance -= $amount; 
  56.  
  57. } 
  58.  
  59. return$this->balance; 
  60.  
  61. } 
  62.  
  63. /** 
  64.  
  65. * @return float 
  66.  
  67. */ 
  68.  
  69. privatefunctiongetBalance(): float 
  70.  
  71. { 
  72.  
  73. return$this->balance; 
  74.  
  75. } 
  76.  
  77. publicfunctionrunLock(int $i, string $type, float $amount) 
  78.  
  79. { 
  80.  
  81. try{ 
  82.  
  83. $start= microtime(true); 
  84.  
  85. switch($type) { 
  86.  
  87. case'flock': 
  88.  
  89. $this->flock($amount); 
  90.  
  91. break; 
  92.  
  93. case'redisLock': 
  94.  
  95. $this->redisLock($amount); 
  96.  
  97. break; 
  98.  
  99. case'redLock': 
  100.  
  101. $this->redLock($amount); 
  102.  
  103. break; 
  104.  
  105. case'channelLock': 
  106.  
  107. $this->channelLock($amount); 
  108.  
  109. break; 
  110.  
  111. case'noMutex': 
  112.  
  113. default: 
  114.  
  115. $this->deductBalance($amount); 
  116.  
  117. break; 
  118.  
  119. } 
  120.  
  121. $balance= $this->getBalance(); 
  122.  
  123. $id= SwooleEngine::id(); 
  124.  
  125. $cost= microtime(true) - $start; 
  126.  
  127. $this->logger->notice('[{type} {cost}] ==> [{i}<=>{id}] ==> 当前用户的余额为:{balance}', compact('type', 'i', 'balance', 'id', 'cost')); 
  128.  
  129. return$balance; 
  130.  
  131. } catch(WiseLocksmithException|Throwable $e) { 
  132.  
  133. returnsprintf('Err Msg: %s ====> %s', $e, $e->getPrevious()); 
  134.  
  135. } 
  136.  
  137. } 
  138.  
  139. } 

然后我们再写一个控制器进行调用

  1. <?php 
  2.  
  3. declare(strict_types=1); 
  4.  
  5. namespaceApp\Controller; 
  6.  
  7. useHyperf\HttpServer\Annotation\AutoController; 
  8.  
  9. useApp\Services\AccountBalanceService; 
  10.  
  11. useHyperf\Coroutine\Parallel; 
  12.  
  13. usefunction\Hyperf\Support\make; 
  14.  
  15. #[AutoController] 
  16.  
  17. classBalanceController extendsAbstractController 
  18.  
  19. { 
  20.  
  21. // curl '127.0.0.1:9511/balance/consumer?type=noMutex' 
  22.  
  23. publicfunctionconsumer() 
  24.  
  25. { 
  26.  
  27. $type= $this->request->input('type', 'noMutex'); 
  28.  
  29. $amount= (float)$this->request->input('amount', 1); 
  30.  
  31. $parallel= newParallel(); 
  32.  
  33. $balance= make(AccountBalanceService::class); 
  34.  
  35. // 模拟 20 个并发 
  36.  
  37. for($i= 1; $i<= 20; $i++) { 
  38.  
  39. $parallel->add(function() use($balance, $i, $type, $amount) { 
  40.  
  41. return$balance->runLock($i, $type, $amount); 
  42.  
  43. }, $i); 
  44.  
  45. } 
  46.  
  47. $result= $parallel->wait(); 
  48.  
  49. return$this->response->json($result); 
  50.  
  51. } 
  52.  
  53. } 

当我们访问 /balance/consumer?type=noMutex 地址时,我们可以看到用户的余额会被扣成负数,这明显不符合逻辑。 然而当我们访问下面几个地址时,我们可以看到用户余额不会被扣成负数,则说明很好的保护了竞态下的共享资源的准确性。

/balance/consumer?type=flock :文件锁

/balance/consumer?type=redisLock :分布式锁

/balance/consumer?type=redLock :红锁

/balance/consumer?type=channelLock :协程级别的互斥锁

注意

关于使用到 redisLock 和 redLock 时:

使用 redisLock 默认采用的 config/autoload/redis.php 配置文件中的第一个 key 配置 redis 实例(即 default)。可按需传入第 4 个参数 string|null $redisPoolName 进行重新指定。

使用 redLock 默认采用的 config/autoload/redis.php 配置文件中的所有 key 对应的配置 redis 实例。可按需传入第 4 个参数 ?array $redisPoolNames = null 进行重新指定。

Tags: PHP互斥锁库 hyperf-wise-locksmith

分享到: