【高并发】高并发分布式锁架构解密,不是所有的锁都是分布式锁!! (4)

我们在下单操作的方法中为分布式锁引入了超时机制,此时的代码还是无法真正避免死锁的问题,那“坑位”到底在哪里呢?试想,当程序执行完stringRedisTemplate.opsForValue().setIfAbsent()方法后,正要执行stringRedisTemplate.expire(PRODUCT_ID, 30, TimeUnit.SECONDS)代码时,服务器宕机了,你还别说,生产坏境的情况非常复杂,就是这么巧,服务器就宕机了。此时,后续请求进入提交订单的方法时,都会因为无法成功设置锁标志而导致后续下单流程无法正常执行。

既然我们找到了上述代码的“坑位”,那我们如何将这个”坑“填上?如何解决这个问题呢?别急,Redis已经提供了这样的功能。我们可以在向Redis中保存数据的时候,可以同时指定数据的超时时间。所以,我们可以将代码改造成如下所示。

/** * 为了演示方便,我这里就简单定义了一个常量作为商品的id * 实际工作中,这个商品id是前端进行下单操作传递过来的参数 */ public static final String PRODUCT_ID = "100001"; @RequestMapping("/submitOrder") public String submitOrder(){ //通过stringRedisTemplate来调用Redis的SETNX命令,key为商品的id,value为字符串“binghe” //实际上,value可以为任意的字符换 Boolean isLocked = stringRedisTemplate.opsForValue().setIfAbsent(PRODUCT_ID, "binghe", 30, TimeUnit.SECONDS); //没有拿到锁,返回下单失败 if(!isLock){ return "failure"; } try{ int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); if(stock > 0){ stock -= 1; stringRedisTemplate.opsForValue().set("stock", String.valueOf(stock)); logger.debug("库存扣减成功,当前库存为:{}", stock); }else{ logger.debug("库存不足,扣减库存失败"); throw new OrderException("库存不足,扣减库存失败"); } }finally{ //业务执行完成,删除PRODUCT_ID key stringRedisTemplate.delete(PRODUCT_ID); } return "success"; }

在上述代码中,我们在向Redis中设置锁标志位的时候就设置了超时时间。此时,只要向Redis中成功设置了数据,则即使我们的业务系统宕机,Redis中的数据过期后,也会自动删除。后续的线程进入提交订单的方法后,就会成功的设置锁标志位,并向下执行正常的下单流程。

到此,上述的代码基本上在功能角度解决了程序的死锁问题,那么,上述程序真的就完美了吗?哈哈,很多小伙伴肯定会说不完美!确实,上面的代码还不是完美的,那大家知道哪里不完美吗?接下来,我们继续分析。

在开发集成角度分析代码

在我们开发公共的系统组件时,比如我们这里说的分布式锁,我们肯定会抽取一些公共的类来完成相应的功能来供系统使用。

这里,假设我们定义了一个RedisLock接口,如下所示。

public interface RedisLock{ //加锁操作 boolean tryLock(String key, long timeout, TimeUnit unit); //解锁操作 void releaseLock(String key); }

接下来,使用RedisLockImpl类实现RedisLock接口,提供具体的加锁和解锁实现,如下所示。

public class RedisLockImpl implements RedisLock{ @Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean tryLock(String key, long timeout, TimeUnit unit){ return stringRedisTemplate.opsForValue().setIfAbsent(key, "binghe", timeout, unit); } @Override public void releaseLock(String key){ stringRedisTemplate.delete(key); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssxwg.html