redislock.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package redis
  2. import (
  3. "math/rand"
  4. "sync/atomic"
  5. "time"
  6. red "github.com/go-redis/redis/v8"
  7. "github.com/zeromicro/go-zero/core/logx"
  8. "github.com/zeromicro/go-zero/core/stringx"
  9. )
  10. const (
  11. delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
  12. return redis.call("DEL", KEYS[1])
  13. else
  14. return 0
  15. end`
  16. randomLen = 16
  17. )
  18. // A RedisLock is a redis lock.
  19. type RedisLock struct {
  20. store *Redis
  21. seconds uint32
  22. count int32
  23. key string
  24. id string
  25. }
  26. func init() {
  27. rand.Seed(time.Now().UnixNano())
  28. }
  29. // NewRedisLock returns a RedisLock.
  30. func NewRedisLock(store *Redis, key string) *RedisLock {
  31. return &RedisLock{
  32. store: store,
  33. key: key,
  34. id: stringx.Randn(randomLen),
  35. }
  36. }
  37. // Acquire acquires the lock.
  38. func (rl *RedisLock) Acquire() (bool, error) {
  39. newCount := atomic.AddInt32(&rl.count, 1)
  40. if newCount > 1 {
  41. return true, nil
  42. }
  43. seconds := atomic.LoadUint32(&rl.seconds)
  44. ok, err := rl.store.SetnxEx(rl.key, rl.id, int(seconds+1)) // +1s for tolerance
  45. if err == red.Nil {
  46. atomic.AddInt32(&rl.count, -1)
  47. return false, nil
  48. } else if err != nil {
  49. atomic.AddInt32(&rl.count, -1)
  50. logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error())
  51. return false, err
  52. } else if !ok {
  53. atomic.AddInt32(&rl.count, -1)
  54. return false, nil
  55. }
  56. return true, nil
  57. }
  58. // Release releases the lock.
  59. func (rl *RedisLock) Release() (bool, error) {
  60. newCount := atomic.AddInt32(&rl.count, -1)
  61. if newCount > 0 {
  62. return true, nil
  63. }
  64. resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id})
  65. if err != nil {
  66. return false, err
  67. }
  68. reply, ok := resp.(int64)
  69. if !ok {
  70. return false, nil
  71. }
  72. return reply == 1, nil
  73. }
  74. // SetExpire sets the expiration.
  75. func (rl *RedisLock) SetExpire(seconds int) {
  76. atomic.StoreUint32(&rl.seconds, uint32(seconds))
  77. }