在日常业务开发中,为了解决并发问题,比如,同一个时刻,多笔相同订单号的订单同时请求,我们只会受理一笔,其他的请求拒绝。我们通常都是用分布锁来解决,当然,也可以使用数据库的唯一索引来解决,数据新增的时候会报插入异常,这样如果系统并发很大,会给数据库造成很大的压力,通常都不会这么操作。
实现分布式锁的方案有很多种,比如用 zookeeper、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); } } }