springboot+redis+redisson 分布式锁 续期 秒杀场景

springboot+redis+redisson 分布式锁 续期 秒杀场景

pom.xml配置文件


        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.8.2</version>
        </dependency>

yaml配置文件


spring:
  #############redis配置
  redis:
    database: 1
    host: 10.1.1.12
    port: 6039
    jedis:
      pool:
        max-idle: 8
        max-active: 9000
        max-wait: 6000ms
        min-idle: 5
    timeout: 6000ms

配置文件:

package com.hongkun.config;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * redisson配置
 */
@Slf4j
@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;


    @Value("${spring.redis.database:0}")
    private int database;

    @Bean
    public RedissonClient getRedisson() throws Exception{
        RedissonClient redisson = null;
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port).setDatabase(database);
       // log.info("redisson配置,addressUrl:"+addressUrl);
        redisson = Redisson.create(config);

        System.out.println(redisson.getConfig().toJSON().toString());
        return redisson;
    }
}
RedissonLocker
import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

public interface RedissonLocker {

    RLock lock(String lockKey);

    RLock lock(String lockKey, long timeout);

    RLock lock(String lockKey, TimeUnit unit, long timeout);

    boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);

    void unlock(String lockKey);

    void unlock(RLock lock);



}

RedissonLockerImpl:


import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 总结
 * 1.要使 watchLog机制生效 ,lock时 不要设置 过期时间
 * 2.watchlog的延时时间 可以由 lockWatchdogTimeout指定默认延时时间,但是不要设置太小。如100
 * 3.watchdog 会每 lockWatchdogTimeout/3时间,去延时。
 * 4.watchdog 通过 类似netty的 Future功能来实现异步延时
 * 5.watchdog 最终还是通过 lua脚本来进行延时
 * @auther: TF12778
 * @date: 2020/7/29 16:23
 * @description:
 */
@Component
public class RedissonLockerImpl implements RedissonLocker {

    @Autowired
    private RedissonClient redissonClient; // RedissonClient已经由配置类生成,这里自动装配即可

    // lock(), 拿不到lock就不罢休,不然线程就一直block
    @Override
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    // leaseTime为加锁时间,单位为秒
    @Override
    public RLock lock(String lockKey, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
        return null;
    }

    // timeout为加锁时间,时间单位由unit确定
    @Override
    public RLock lock(String lockKey, TimeUnit unit, long timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    /**
     * 设置了leaseTime之后,如果waitTime大于leaseTime,那么锁自动失效后,其他线程会等待waitTime之后,才会获取锁
     * 而不是立即获取锁。
     */
    /**
     * 尝试获取锁
     * @param lockKey
     * @param unit 时间单位
     * @param waitTime 最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void unlock(RLock lock) {
        lock.unlock();
    }
}

业务实现

RedissionFacadeServiceImpl


import com.alibaba.fastjson.JSONObject;
import com.hongkun.common.RedisKey;
import com.hongkun.mapper.CouponInfoMapper;
import com.hongkun.untils.DateUtil;
import com.hongkun.untils.RedisUtil;
import com.hongkun.untils.SnowflakeUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;


/**
 * @auther: TF12778
 * @date: 2020/7/29 11:09
 * @description:
 */
@Slf4j
@Service("redissionFacadeService")
public class RedissionFacadeServiceImpl implements RedissionFacadeService {

    private static final String LOCK_KEY = "lockswww";

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private CouponInfoMapper couponInfoMapper;


    @Resource
    private RedissonLocker redissonLocker;

    /**
     * 下单步骤:校验库存,扣库存,创建订单,支付
     *
     */
//    @Transactional  此处不需要加事物,否则订单数量超表
    @Override
    public void saveOrder(Integer userId,Integer couponId) {





            /**
             * 秒杀流程
             * 1、通过redis判断用户是否重复秒杀
             * 2、redis预减库存;抢购,减少库存
             * 3、秒杀成功,存入用户id 到商品去重表以及消息队列进行商品订单处理
             */

            //1、通过redis判断重复秒杀
            Boolean userExist= redisUtil.sIsMember(String.format(RedisKey.COUPON_INFO_USER, couponId), String.valueOf(userId));
            //用存在直接返回
            if(userExist){
                log.info("用户:"+userId+",已经存在商品:"+couponId);
            }else{

                //2.创建锁实例
                RLock lock = redissonLocker.lock(LOCK_KEY);// 释放
                try {
                    //3.尝试获取分布式锁,第一个参数为等待时间
                    boolean isLock = lock.tryLock(10, TimeUnit.SECONDS);
                    log.info("用户抢购加锁userId:"+userId+",尝试加锁isLock:"+isLock);

                    if(isLock){

                        Thread.sleep(20000);

                        //获取剩余数量
                        String couponCount = redisUtil.get(String.format(RedisKey.COUPON_COUNT, couponId));
                        log.info("用户抢购加锁成功,userId:"+userId+"商品:"+couponId+",剩余优惠卷数量couponCount:"+couponCount);

                        if(Integer.parseInt(couponCount) > 0) {

                            //2、 redis预减库存;抢购,减少库存
                            Long count = redisUtil.decrBy(String.format(RedisKey.COUPON_COUNT, couponId), 1);
                            log.info("用户抢购加锁成功,userId:"+userId+"商品:"+couponId+",剩余优惠卷数量count:"+count);

                            //如果剩余数量大于等于0,认为抢购成功。
                            if(count >=0){

                                log.info("用户抢购成功userId:"+userId+"商品:"+couponId+",剩余优惠卷数量:"+count);

                                //3、秒杀成功,存入用户id 到商品去重
                                redisUtil.sAdd(String.format(RedisKey.COUPON_INFO_USER, couponId),String.valueOf(userId));

                                JSONObject jsonObject = new JSONObject();
                                jsonObject.put("orderId", SnowflakeUtil.nextId());//订单id,雪花算法
                                jsonObject.put("userId",userId);
                                jsonObject.put("couponId",couponId);
                                jsonObject.put("rushBuyingTime", DateUtil.getCurrent());//抢购时间

                                //用户
                                redisUtil.set(String.format(RedisKey.USER_COUPON_INFO,couponId,userId),jsonObject.toString());

                            }else{
                                log.info("用户抢购失败userId:"+userId+"商品:"+couponId+",剩余优惠卷数量:"+count);
                            }

                        }

                        redissonLocker.unlock(LOCK_KEY); // 释放锁

                    }else {
                        log.info("用户抢购加锁失败userId:"+userId+"商品:"+couponId);
                        //this.saveOrder( userId, couponId);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    redissonLocker.unlock(LOCK_KEY); // 释放锁
                }


            }







    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值