来源:https://www.cnblogs.com/wangrudong003/p/10627539.html
本篇内容紧张讲解的是redis分布式锁,这个在各大厂口试险些都是必备的,下面结合仿照抢单的场景来利用她;本篇不涉及到的redis环境搭建,快速搭建个人测试环境,这里建议利用docker;本篇内容节点如下:
jedis的nx天生锁如何删除锁仿照抢单动作(10w个人开抢)jedis的nx天生锁对付java中想操作redis,好的办法是利用jedis,首先pom中引入依赖:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency>
对付分布式锁的天生常日须要把稳如下几个方面:
创建锁的策略:redis的普通key一样平常都许可覆盖,A用户set某个key后,B在set相同的key时同样能成功,如果是锁场景,那就无法知道到底是哪个用户set成功的;这里jedis的setnx办法为我们办理了这个问题,大略事理是:当A用户先set成功了,那B用户set的时候就返回失落败,知足了某个韶光点只许可一个用户拿到锁。锁过期韶光:某个抢购场景时候,如果没有过期的观点,当A用户天生了锁,但是后面的流程被壅塞了一贯无法开释锁,那其他用户此时获取锁就会一贯失落败,无法完成抢购的活动;当然正常情形一样平常都不会壅塞,A用户流程会正常开释锁;过期韶光只是为了更有保障。下面来上段setnx操作的代码:
这里把稳点在于jedis的set方法,其参数的解释如:
NX:是否存在key,存在就不set成功PX:key过期韶光单位设置为毫秒(EX:单位秒)setnx如果失落败直接封装返回false即可,下面我们通过一个get办法的api来调用下这个setnx方法:
@GetMapping("/setnx/{key}/{val}")publicbooleansetnx(@PathVariableStringkey,@PathVariableStringval){returnjedisCom.setnx(key,val);}
访问如下测试url,正常来说第一次返回了true,第二次返回了false,由于第二次要求的时候redis的key已存在,以是无法set成功
由上图能够看到只有一次set成功,并key具有一个有效韶光,此时已到达了分布式锁的条件。
如何删除锁上面是创建锁,同样的具有有效韶光,但是我们不能完备依赖这个有效韶光,场景如:有效韶光设置1分钟,本身用户A获取锁后,没碰着什么分外情形正常天生了抢购订单后,此时其他用户该当能正常下单了才对,但是由于有个1分钟后锁才能自动开释,那其他用户在这1分钟无法正常下单(由于锁还是A用户的),因此我们须要A用户操作完后,主动去解锁:
这里也利用了jedis办法,直接实行lua脚本:根据val判断其是否存在,如果存在就del;
实在个人认为通过jedis的get办法获取val后,然后再比较value是否是当前持有锁的用户,如果是那末了再删除,效果实在相称;只不过直接通过eval实行脚本,这样避免多一次操作了redis而已,缩短了原子操作的间隔。(如有不同见地请留言磋商);同样这里创建个get办法的api来测试:
@GetMapping("/delnx/{key}/{val}")publicintdelnx(@PathVariableStringkey,@PathVariableStringval){returnjedisCom.delnx(key,val);}
把稳的是delnx时,须要通报创建锁时的value,由于通过et的value与delnx的value来判断是否是持有锁的操作要求,只有value一样才许可del;
仿照抢单动作(10w个人开抢)有了上面对分布式锁的粗略根本,我们仿照下10w人抢单的场景,实在便是一个并发操作要求而已,由于环境有限,只能如此测试;如下初始化10w个用户,并初始化库存,商品等信息,如下代码:
有了上面10w个不同用户,我们设定商品只有10个库存,然后通过并行流的办法来仿照抢购,如下抢购的实现:
这里实现的逻辑是:
parallelStream():并行流仿照多用户抢购(startTime + timeout) >= System.currentTimeMillis():判断未抢成功的用户,timeout秒内连续获取锁获取锁前和后都判断库存是否还足够jedisCom.setnx(shangpingKey, b):用户获取抢购锁获取锁后并下单成功,末了开释锁:jedisCom.delnx(shangpingKey, b)再来看下记录的日志结果:
终极返回抢购成功的用户: