蛋蛋星球-客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

svc_redis_mutex_lock.go 2.0 KiB

1 month ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package svc
  2. import (
  3. "applet/app/md"
  4. "applet/app/utils"
  5. "applet/app/utils/cache"
  6. "errors"
  7. "fmt"
  8. "math/rand"
  9. "reflect"
  10. "time"
  11. )
  12. const redisMutexLockExpTime = 15
  13. // TryGetDistributedLock 分布式锁获取
  14. // requestId 用于标识请求客户端,可以是随机字符串,需确保唯一
  15. func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool {
  16. if isNegative { // 多次尝试获取
  17. retry := 1
  18. for {
  19. ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
  20. // 获取锁成功
  21. if err == nil && ok == "OK" {
  22. return true
  23. }
  24. // 尝试多次没获取成功
  25. if retry > 10 {
  26. return false
  27. }
  28. time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
  29. retry += 1
  30. }
  31. } else { // 只尝试一次
  32. ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
  33. // 获取锁成功
  34. if err == nil && ok == "OK" {
  35. return true
  36. }
  37. return false
  38. }
  39. }
  40. // ReleaseDistributedLock 释放锁,通过比较requestId,用于确保客户端只释放自己的锁,使用lua脚本保证操作的原子型
  41. func ReleaseDistributedLock(lockKey, requestId string) (bool, error) {
  42. luaScript := `
  43. if redis.call("get",KEYS[1]) == ARGV[1]
  44. then
  45. return redis.call("del",KEYS[1])
  46. else
  47. return 0
  48. end`
  49. do, err := cache.Do("eval", luaScript, 1, lockKey, requestId)
  50. fmt.Println(reflect.TypeOf(do))
  51. fmt.Println(do)
  52. if utils.AnyToInt64(do) == 1 {
  53. return true, err
  54. } else {
  55. return false, err
  56. }
  57. }
  58. func GetDistributedLockRequestId(prefix string) string {
  59. return prefix + utils.IntToStr(rand.Intn(100000000))
  60. }
  61. func HandleLimiterDistributedLock(masterId, ip, requestIdPrefix string) (cb func(), err error) {
  62. balanceLockKey := fmt.Sprintf(md.AppLimiterLock, masterId, ip)
  63. requestId := GetDistributedLockRequestId(requestIdPrefix)
  64. balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
  65. if !balanceLockOk {
  66. return nil, errors.New("系统繁忙,请稍后再试")
  67. }
  68. cb = func() {
  69. _, _ = ReleaseDistributedLock(balanceLockKey, requestId)
  70. }
  71. return cb, nil
  72. }