springboot使用Redisson分布式锁

为什么要使用分布式锁

在分布式场景下为了保证数据最终一致性。在单进程的系统中,存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步(lock—synchronized),使其在修改这种变量时能够线性执行消除并发修改变量。但分布式系统是多部署、多进程的,开发语言提供的并发处理API在此场景下就无能为力了。


分布式锁的使用场景

电商网站用下单操作时需要使用,秒杀活动更是如此,否则会出现超卖(库存100,秒杀活动时库存变负数了


分布式锁的实现方式

大概有三种:1.基于关系型数据库,2.基于缓存,3基于zookeeper
大部分网站使用的是基于缓存的,有更好的性能,而缓存一般是以集群方式部署,保证了高可用性

总体来说,支持redis单实例、redis哨兵、redis cluster、redis master-slave等各种部署架构,都可以给你完美实现。

本次说的是基于缓存redis,使用开源 redisson 实现的分布式锁。

引入pom包

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.redisson
            redisson
            3.5.0
        

    

# redis配置
spring.redis.database=1
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456

redis配置

@Configuration
public class RedisConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.database}")
    private int database;

    @Bean(name = "redisTemplate")
    public StringRedisTemplate redisTemplate() {
        StringRedisTemplate temple = new StringRedisTemplate();
        temple.setConnectionFactory(new RedissonConnectionFactory(Redisson.create(config(database))));
        return temple;
    }


    /**
     * 默认连接
     *
     * @return
     */
    @Bean
    public RedissonClient redissonClient() {
        return Redisson.create(config(database));
    }

    /**
     * 单机版
     *
     * @return
     */
    public Config config(int index) {
        Config config = new Config();
        config.setThreads(4);
        config.setNettyThreads(8);
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig
                .setAddress("redis://" + host + ":" + port)
                .setSslEnableEndpointIdentification(false);
        if (0 < index && index <= 16) {
            singleServerConfig.setDatabase(index);
        }
        if (StringUtils.isNotBlank(password)) {
            singleServerConfig.setPassword(password);
        }
        return config;
    }
}

新增一个Redisson控制类

@Slf4j
public class DistributedLockHandler {

    private RLock lock;

    private String lockKey;

    private volatile boolean locked = false;
    /**
     * 默认锁过期时间(毫秒)
     */
    private int leaseMsecs = 60 * 1000;

    /**
     * 获取锁等待时间(毫秒)
     */
    private int waitMsecs = 10 * 1000;

    public DistributedLockHandler(RedissonClient redissonClient, String lockKey) {
        this.lockKey = lockKey + "_lock";
        this.lock = redissonClient.getLock(this.lockKey);
    }

    public DistributedLockHandler(RedissonClient redissonClient, String lockKey, int waitMsecs) {
        this(redissonClient, lockKey);
        this.waitMsecs = waitMsecs;
    }

    public DistributedLockHandler(RedissonClient redissonClient, String lockKey, int waitMsecs, int leaseMsecs) {
        this(redissonClient, lockKey, waitMsecs);
        this.leaseMsecs = leaseMsecs;
    }

    public String getLockKey() {
        return lockKey;
    }

    public synchronized boolean lock() throws InterruptedException {
        locked = lock.tryLock(waitMsecs, leaseMsecs, TimeUnit.MILLISECONDS);
        return locked;
    }

    public synchronized void unlock() {
        if (locked) {
            lock.unlock();
            locked = false;
        }
    }
}

最后使用,我们假设场景是用户支付了30分钟未付款自动取消订单,由于生产部署肯定是集群多台机器部署,我们为了减小服务器压力可以使用加锁

/**
 * @author: lockie
 * @Date: 2019/8/20 15:58
 * @Description: 未支付订单取消定时任务
 */
@Component
public class OrderPaymentTask {
    private static final Logger logger = LoggerFactory.getLogger(OrderPaymentTask.class);

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 加锁时间
     * 10s
     */
    private static final int ORDER_PAYMENT_TASK_EXPIRE_TIME = 10000;

    /**
     * 订单取消时间
     * 1小时执行一次
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void executeTask() {
        long startTime = System.currentTimeMillis();
        // 获取加锁
        DistributedLockHandler distributedLockHandler = new DistributedLockHandler(redissonClient, "order_payment_task", 1000, ORDER_PAYMENT_TASK_EXPIRE_TIME);
        try {

            if(distributedLockHandler.lock()) {
                // 查询符合取消条件的订单,自己的逻辑
                

            } else{
                logger.info("OrderPaymentTask 定时任务已锁");
            }
            long endTime = System.currentTimeMillis();
            logger.info("OrderPaymentTask 结束执行,耗时: " + (endTime - startTime) + " 毫秒");

        } catch (Exception e) {
            logger.error("订单取消定时器执行异常", e);
            distributedLockHandler.unlock();
        }
    }
}

 

你可能感兴趣的:(spring,boot,redis)