Java秒杀系统(十五):基于Redisson的分布式锁优化秒杀逻辑

摘要:本篇博文是“Java秒杀系统实战系列文章”的第十五篇,本文我们将借助综合中间件Redisson优化“秒杀系统中秒杀的核心业务逻辑”,解决Redis的原子操作在优化秒杀逻辑过程中出现的部分瑕疵。

内容:Redisson,字如其名,是搭建在缓存中间件Redis的基础之上的一款综合中间件,除了拥有Redis本身提供的强大功能之外,还提供了诸如分布式锁、分布式服务、延迟队列、远程调用等强大的功能(从名字就可以看出来了:Redis + son,犹如Redis的儿子,儿子不仅继承了老爸强大的血脉,而且还自己修炼、发展出了属于自己的一套本领)

在本篇文章中,我们将使用Redisson中间件其中一个强大的功能组件“分布式锁”,用以解决秒杀系统中高并发产生的多线程对于共享资源/代码块的访问所导致的“并发安全”问题!

而之所以需要Redisson这一组件,是因为在上一篇文章中,我们在采用Redis解决秒杀系统中出现的“库存超卖”、“重复秒杀”等问题时所对应的代码存在着瑕疵,即在使用Redis的SetNX操作之前、而还没来得及执行Expire操作的时候,Redis的节点如果恰好出现宕机或者服务不能用的情况,那将会导致相应的Key永远存在于缓存中,而处于“被锁死”的状态!

Redisson分布式锁的出现可以很好地解决这种问题,其底层的实现机制在于“Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期”,除此之外,Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间,即超过这个时间后锁便自动解开了。

接下来,我们将基于SpringBoot搭建的秒杀系统整合Redisson,加入其相关的依赖以及配置,并使用其“分布式锁”组件彻底解决秒杀过程中出现的“库存超卖”以及“重复秒杀”等问题。

(1)首先,需要加入Redisson的依赖,版本号为3.8.2,如下所示:

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>${redisson.version}</version>
</dependency>

然后需要在配置文件application.properties中加入Redis服务所在的Host、Port等信息,如下所示:  

#spring.redis.password=
redis.config.host=redis://127.0.0.1:6379

(2)紧接着,是基于Spring Boot自定义注入Redisson相关操作的Bean组件,其中,主要是RedissonClient 操作组件的自定义注入,其完整源代码如下所示:  

/**
 * redisson通用化配置
 * @Author:debug (SteadyJack)
 * @Date: 2019/7/2 10:57
 **/
@Configuration
public class RedissonConfig {
 
    @Autowired
    private Environment env;
 
    @Bean
    public RedissonClient redissonClient(){
        Config config=new Config();
        config.useSingleServer()
                .setAddress(env.getProperty("redis.config.host"))
                .setPassword(env.getProperty("spring.redis.password"));
        RedissonClient client=Redisson.create(config);
        return client;
    }
}

(3)前期工作已经准备完毕,接下来我们需要将其应用到秒杀系统中 秒杀的核心操作逻辑,在KillService服务类中我们开辟了一个新的处理方法,即killItemV4,其完整的源代码如下所示:  

@Autowired
private RedissonClient redissonClient;
 
//商品秒杀核心业务逻辑的处理-redisson的分布式锁
@Override
public Boolean killItemV4(Integer killId, Integer userId) throws Exception {
    Boolean result=false;
 
    final String lockKey=new StringBuffer().append(killId).append(userId).append("-RedissonLock").toString();
    RLock lock=redissonClient.getLock(lockKey);
 
    try {
        //TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
        //TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
        Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);
        if (cacheRes){
            //TODO:核心业务逻辑的处理
            if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
                ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
                if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
                    int res=itemKillMapper.updateKillItemV2(killId);
                    if (res>0){
                        commonRecordKillSuccessInfo(itemKill,userId);
 
                        result=true;
                    }
                }
            }else{
                throw new Exception("redisson-您已经抢购过该商品了!");
            }
        }
    }finally {
        //TODO:释放锁
        lock.unlock();
    }
    return result;
}

 

更多请见:http://www.mark-to-win.com/tutorial/51048.html 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值