重要的命令:
SETNX命令(SET if Not eXists)
语法:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
GETSET命令
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
执行过程:
-
- setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2。
-
- get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3。
-
- 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。
-
- 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。
-
- 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。
@RestController
14 @Slf4j
15 public class OrderController {
16
17 @Autowired
18 private StringRedisTemplate stringRedisTemplate;
19
20 @PostMapping(value = "/createOrder", produces = "application/json;charset=utf-8")
21 public String createOrder(@RequestBody OrderRequest request) {
22
23 String json = JsonUtils.objectToJson(request);
24 String md5 = DigestUtils.md5DigestAsHex(json.getBytes()).toUpperCase();
25
26 /*
27 * setIfAbsent <=> SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]]
28 * set expire time 5 mins
29 */
30 Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(md5, "1", 60 * 5, TimeUnit.SECONDS);
31 if (flag) {
32 // lock success, start to handle business
33 log.debug("{} lock success, start to handle business", md5);
34 try {
35 //mock to handle business
36 Thread.sleep(1000 * 10);
37 } catch (InterruptedException e) {
38 e.printStackTrace();
39 }
40 //end to handle business, need to unlock
41 stringRedisTemplate.delete(md5);
42 log.debug("{} unlock success,end to handle business", md5);
43 return "SUCCESS";
44 } else {
45 log.debug("{} lock failure", md5);
46 return "try again later";
47 }
48 }
49 }