lock.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. package lockx
  2. import (
  3. "context"
  4. "github.com/zeromicro/go-zero/core/stores/redis"
  5. "time"
  6. )
  7. type DistributedLock struct {
  8. client *redis.Redis
  9. key string
  10. seconds int
  11. }
  12. func NewDistributedLock(client *redis.Redis, key string, seconds int) *DistributedLock {
  13. return &DistributedLock{
  14. client: client,
  15. key: key,
  16. seconds: seconds,
  17. }
  18. }
  19. // Lock 尝试获取分布式锁
  20. func (dl *DistributedLock) Lock() (bool, error) {
  21. if ok, err := dl.client.SetnxEx(dl.key, "1", dl.seconds); err != nil {
  22. return false, err
  23. } else if ok {
  24. return true, nil
  25. }
  26. return false, nil
  27. }
  28. // WaitLock 尝试获取分布式锁,获取不到等待
  29. func (dl *DistributedLock) WaitLock(ctx context.Context) error {
  30. for {
  31. select {
  32. case <-ctx.Done():
  33. // 如果Context被取消,则直接返回错误
  34. return ctx.Err()
  35. default:
  36. // 尝试获取锁
  37. result, err := dl.client.SetnxEx(dl.key, "1", dl.seconds)
  38. if err != nil {
  39. return err
  40. }
  41. if result {
  42. // 获取到锁,直接返回
  43. return nil
  44. }
  45. // 等待一段时间后重试
  46. time.Sleep(time.Millisecond * 50)
  47. }
  48. }
  49. }
  50. // Unlock 释放分布式锁
  51. func (dl *DistributedLock) Unlock() error {
  52. // 使用 Lua 脚本来保证原子性地删除锁(避免误删其他客户端的锁)
  53. luaScript := `
  54. if redis.call("get", KEYS[1]) == ARGV[1] then
  55. return redis.call("del", KEYS[1])
  56. else
  57. return 0
  58. end
  59. `
  60. _, err := dl.client.Eval(luaScript, []string{dl.key}, "1")
  61. return err
  62. }