使用场景
限流
业务需求
我们需要对业务进行限流,比如我们要求1秒内只能让某个服务访问 N 次,如果超过 N 那么就紧张访问,或者让他们等待。
业务代码
基本的思路是使用一个 Key + Incr 操作来完成:
例如:
- Key: comment_freq_limit_xxx (timestamp)
对这个Key调用Incr,超过限制N则禁止访问。
数据结构
是我们(一)中所讲到的 String 结构
分布式锁
业务需求
并发场景,要求一次只能由一个协程执行,执行完成后其他在等待的协程才能够执行。
业务代码
func ex02Work(ctx context.Context, cInstParam common.CInstParams) {
routine := cInstParam.Routine
eventLogger := cInstParam.ConcurrentEventLogger
defer ex02ReleaseLock(ctx, routine, eventLogger)
for {
acquired, err := RedisClient.SetNX(ctx, resourceKey, routine, exp).Result()
if err != nil {
eventLogger.Append(common.EventLog{
EventTime: time.Now(), Log: fmt.Sprintf("[%s] error routine[%d], %v", time.Now().Format(time.RFC3339Nano), routine, err),
})
panic(err)
}
if acquired {
eventLogger.Append(common.EventLog{
EventTime: time.Now(), Log: fmt.Sprintf("[%s] routine[%d] 获取锁", time.Now().Format(time.RFC3339Nano), routine),
})
time.Sleep(10 * time.Millisecond)
eventLogger.Append(common.EventLog{
EventTime: time.Now(), Log: fmt.Sprintf("[%s] routine[%d] 完成业务逻辑", time.Now().Format(time.RFC3339Nano), routine),
})
return
} else {
time.Sleep(100 * time.Millisecond)
}
}
}
然后我们在做一个释放 SetNX 的逻辑即可。
可以使用 Redis 的 SetNX 实现,利用的特性有:
- Redis 单线程执行操作
- SetNX 只有没有设置过才能够执行成功
但是上面使用的 SetNX 也不能够做成真正的高可用的锁,有以下实现上的问题:
- 业务超时解锁,导致并发问题。业务执行时间超过锁超时时间
- redis主备切换临界点问题。主备切换后,A持有的锁还未同步到新的主节点时,B可在新主节点获取锁,导致并发问题。
- redis集群脑裂,导致出现多个主节点