当前位置:首页 > 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. $path= BASE_PATH . '/runtime/alex.lock.cache'
  5.  
  6. $fileHandlerfopen($path'a+'); 
  7.  
  8. // fwrite($fileHandler, sprintf("%s - %s \r\n", 'Locked', microtime())); 
  9.  
  10. $res$this->locker->flock($fileHandlerfunction() use($amount) { 
  11.  
  12. return$this->deductBalance($amount); 
  13.  
  14. }); 
  15.  
  16. return$res
  17.  

分布式锁(redisLock)

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

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

高级功能

红锁(redLock)

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

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

协程级别的互斥锁(channelLock)

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

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

实际应用场景

假设我们有一个在线支付系统,需要在多个请求中扣减用户的余额。如果不使用互斥锁,可能会导致超扣或扣减失败。使用 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. * @var float|int 
  27.  
  28. */ 
  29.  
  30. privatefloat|int $balance= 10; 
  31.  
  32. publicfunction__construct( 
  33.  
  34. privateStdoutLoggerInterface $logger
  35.  
  36. privateLocker                $locker 
  37.  
  38. ) { 
  39.  
  40. $this->locker->setLogger($logger); 
  41.  
  42.  
  43. privatefunctiondeductBalance(float|int $amount
  44.  
  45.  
  46. if($this->balance >= $amount) { 
  47.  
  48. // 模拟业务处理耗时 
  49.  
  50. usleep(500 * 1000); 
  51.  
  52. $this->balance -= $amount
  53.  
  54.  
  55. return$this->balance; 
  56.  
  57.  
  58. /** 
  59.  
  60. * @return float 
  61.  
  62. */ 
  63.  
  64. privatefunctiongetBalance(): float 
  65.  
  66.  
  67. return$this->balance; 
  68.  
  69.  
  70. publicfunctionrunLock(int $i, string $type, float $amount
  71.  
  72.  
  73. try{ 
  74.  
  75. $start= microtime(true); 
  76.  
  77. switch($type) { 
  78.  
  79. case'flock'
  80.  
  81. $this->flock($amount); 
  82.  
  83. break
  84.  
  85. case'redisLock'
  86.  
  87. $this->redisLock($amount); 
  88.  
  89. break
  90.  
  91. case'redLock'
  92.  
  93. $this->redLock($amount); 
  94.  
  95. break
  96.  
  97. case'channelLock'
  98.  
  99. $this->channelLock($amount); 
  100.  
  101. break
  102.  
  103. case'noMutex'
  104.  
  105. default
  106.  
  107. $this->deductBalance($amount); 
  108.  
  109. break
  110.  
  111.  
  112. $balance$this->getBalance(); 
  113.  
  114. $id= SwooleEngine::id(); 
  115.  
  116. $cost= microtime(true) - $start
  117.  
  118. $this->logger->notice('[{type} {cost}] ==> [{i}<=>{id}] ==> 当前用户的余额为:{balance}', compact('type''i''balance''id''cost')); 
  119.  
  120. return$balance
  121.  
  122. } catch(WiseLocksmithException|Throwable $e) { 
  123.  
  124. returnsprintf('Err Msg: %s ====> %s'$e$e->getPrevious()); 
  125.  
  126.  
  127.  

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

  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. // curl '127.0.0.1:9511/balance/consumer?type=noMutex' 
  21.  
  22. publicfunctionconsumer() 
  23.  
  24.  
  25. $type$this->request->input('type''noMutex'); 
  26.  
  27. $amount= (float)$this->request->input('amount', 1); 
  28.  
  29. $parallel= newParallel(); 
  30.  
  31. $balance= make(AccountBalanceService::class); 
  32.  
  33. // 模拟 20 个并发 
  34.  
  35. for($i= 1; $i<= 20; $i++) { 
  36.  
  37. $parallel->add(function() use($balance$i$type$amount) { 
  38.  
  39. return$balance->runLock($i$type$amount); 
  40.  
  41. }, $i); 
  42.  
  43.  
  44. $result$parallel->wait(); 
  45.  
  46. return$this->response->json($result); 
  47.  
  48.  

当我们访问 /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

分享到: