RedisLock.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. namespace common\libs\lock;
  3. use Yii;
  4. use yii\base\BaseObject;
  5. use yii\base\StaticInstanceTrait;
  6. /**
  7. * Redis分布锁
  8. * 使用锁:
  9. * $key = 'lock';
  10. * if (!$lock->lock($key)) {
  11. * throw new Exception('error');
  12. * }
  13. */
  14. class RedisLock extends BaseObject {
  15. use StaticInstanceTrait;
  16. /**
  17. * 前缀
  18. */
  19. const KEY_PREFIX = 'BonusRedisLock:';
  20. /**
  21. * 重试次数,0:无限重试
  22. * @var int
  23. */
  24. public $retryCount = 0;
  25. /**
  26. * 获取锁失败重试等待的时间,微妙 ,1ms=1000us
  27. * @var int
  28. */
  29. public $retryInterval = 100000;
  30. /**
  31. * 获取锁失败,是否重试
  32. * @var bool
  33. */
  34. public $retry = true;
  35. /**
  36. * 锁的超时时间,防止死锁发生,应该是业务的最大处理时间
  37. * @var int
  38. */
  39. public $expire = 5;
  40. /**
  41. * Redis 对象
  42. * @var null
  43. */
  44. public $redis = null;
  45. /**
  46. * @inheritdoc
  47. */
  48. public function init() {
  49. parent::init();
  50. }
  51. /**
  52. * 获取redis对象
  53. * @return mixed|null
  54. */
  55. public function getRedis(){
  56. if(!is_null($this->redis)){
  57. return $this->redis;
  58. }
  59. $this->redis = Yii::$app->redis;
  60. return $this->redis;
  61. }
  62. /**
  63. * @return float
  64. */
  65. public static function microTime() {
  66. // 获取当前毫秒时间戳
  67. list ($s1, $s2) = explode(' ', microtime());
  68. $currentTime = (float) sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
  69. return $currentTime;
  70. }
  71. /**
  72. * @param $key
  73. * @return string
  74. */
  75. public static function getKey($key){
  76. return self::KEY_PREFIX . $key;
  77. }
  78. /**
  79. * 加锁
  80. * @param $key
  81. * @return bool
  82. */
  83. public function lock($key) {
  84. $count = 0;
  85. $key = self::getKey($key);
  86. while (true) {
  87. $nowTime = self::microtime();
  88. $lockValue = self::microtime() + $this->expire;
  89. $lock = $this->getRedis()->setnx($key, $lockValue);
  90. if (!empty($lock) || ($this->getRedis()->get($key) < $nowTime && $this->getRedis()->getset($key, $lockValue) < $nowTime )) {
  91. $this->getRedis()->expire($key, $this->expire);
  92. return true;
  93. } elseif ($this->retry && (($this->retryCount > 0 && ++$count < $this->retryCount) || ($this->retryCount == 0))) {
  94. usleep($this->retryInterval);
  95. } else {
  96. break;
  97. }
  98. }
  99. return false;
  100. }
  101. /**
  102. * 解锁
  103. * @param $key
  104. * @return bool
  105. */
  106. public function unlock($key) {
  107. $key = self::getKey($key);
  108. if ($this->getRedis()->ttl($key)) {
  109. return $this->getRedis()->del($key);
  110. }
  111. return true;
  112. }
  113. }