基于redisson实现分布式锁
在日常业务开发中,为了解决并发问题,比如,同一个时刻,多笔相同订单号的订单同时请求,我们只会受理一笔,其他的请求拒绝。我们通常都是用分布锁来解决,当然,也可以使用数据库的唯一索引来解决,数据新增的时候会报插入异常,这样如果系统并发很大,会给数据库造成很大的压力,通常都不会这么操作。
实现分布式锁的方案有很多种,比如用 zookeeper、redis等中间件,本文主要介绍使用 redission 实现分布锁,主要原因有以下几点:
- 生产有redis 哨兵集群,不需要在部署其他中间件环境
- redission 有看门狗机制,能自动给分布式锁延期(这个可以网上搜索参考其他文章)
一、环境说明
maven 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.11.2</version> </dependency>
二、代码实现
@Configuration @Setter @ConfigurationProperties(prefix = spring.redis.lettuce.pool) public class RedisClientConfig { @Value(${redis.clusterName}) private String clusterName; @Value(${redis1.host}) private String redis1Host; @Value(${redis1.port}) private Integer redis1Port; @Value(${redis2.host}) private String redis2Host; @Value(${redis2.port}) private Integer redis2Port; @Value(${redis3.host}) private String redis3Host; @Value(${redis3.port}) private Integer redis3Port; @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSentinelServers() .setMasterName(clusterName) .addSentinelAddress(redis://+redis1Host+:+redis1Port) .addSentinelAddress(redis://+redis2Host+:+redis2Port) .addSentinelAddress(redis://+redis3Host+:+redis3Port); return Redisson.create(config); } }
/** * redis 分布式锁 */ @Component @Slf4j public class RedisLock { @Autowired private RedissonClient redissonClient; /** * 尝试拿锁 waitTime 后停止重试,返回false * @param lockKey 锁key值 * @param unit 时间单位 * @param waitTime 等待时间 * @return 是否获取锁成功 */ public boolean tryLock(String lockKey, long waitTime,long leaseTime,TimeUnit unit) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime,leaseTime, unit); } catch (InterruptedException e) { log.error(获取redis分布式锁异常,e); return false; } } /** * 尝试拿锁,不重试,获取不到,返回false * 具有Watch Dog 自动延期机制 默认续30s * @param lockKey * @return */ public boolean tryLock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(0L, TimeUnit.SECONDS); } catch (InterruptedException e) { log.error(获取redis分布式锁异常,e); return false; } } /** * 释放锁 * @param lockKey */ public void unlock(String lockKey) { try { RLock lock = redissonClient.getLock(lockKey); if(lock.isLocked()){ lock.unlock(); } } catch (Exception e) { log.error(释放锁异常,e); } } }