很多场景下我们需要限制用户的操作频率,或者保证一段代码的幂等性。例如在前者的场景下,若用户在1秒内对接口发起同样的请求,即只放过第一个请求,而在这一秒内的后续请求视为无效请求,系统直接给用户提示“您的操作过快,请稍后再试”。后者场景则多见于写操作接口或具有写操作逻辑的MQ消费中。
虽然我们能够使用限流器或更复杂的控制逻辑,但在大多数简单场景下,可以利用redis的setNX配合一个合适的key过期时间即可满足上述需求。
为什么呢?简单来说,上述场景就是一个简单的互斥逻辑,或者说是一个简单的上锁逻辑。redis其单线程的特性,使得请求到达redis后自然成为线性有序的,消除了并发;由因setNX的语义为set if not exist
,即key不存在则set操作成功,反之则失败,是一个现成的互斥操作。
下面就来看下伪代码:
public void doSomething(long orderId) {
// 利用setNX尝试set一个key,并设置过期时间为1秒
// 若set操作失败,则表示该key在1秒内已经存在于redis
if(!jedis.setNX("order_key_" + orderId, "test", 1)) {
throw new DemoServiceException("操作过快,稍后重试");
}
// doSomething ...
}
简单来说,我们就是在利用redis中是否存在某个key,来决定后续的操作。那么,这个key什么时候有,什么时候没,是需要注意的关键。
注:利用setNX在简单的互斥场景下是可用的,但完全用它做分布式锁,会存在很多弊端,然而这个不是本次讨论的重点。
key什么时候有?
这个很简单,请求来了,触发了setNX,若此时redis中没有该key,则新增该key。
key什么时候没有?
-
肉眼可见的消失
所谓肉眼可见,就是代码里明确写了,我要删了这个key。这个需要注意的是什么时候删是能符合需求的,如果删的时机不对,可能会带来一些惊喜,但好在,这个动作是写在代码里的,扒一扒代码,梳理下逻辑,是比较容易修正的。
-
一眼看不出来的消失
相对于上面的直接调用删除,key的过期时间到了,自己消失,会相对隐蔽一些,往往有时候不太好拿捏key要定多久消失合适,可能要考虑要限制多长时间,也要考虑后续代码的执行时长。过期时间太长,影响后续请求;太短,兜不住。需要多测多观察,适度调整。
-
肉眼不可见的消失
这就麻烦了,没写代码,但是自己没了,很头大。发生这种事,一般会