springboot使用Redisson分布式锁
为什么要使用分布式锁
在分布式场景下为了保证数据最终一致性。在单进程的系统中,存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步(lock—synchronized),使其在修改这种变量时能够线性执行消除并发修改变量。但分布式系统是多部署、多进程的,开发语言提供的并发处理API在此场景下就无能为力了。
分布式锁的使用场景
电商网站用下单操作时需要使用,秒杀活动更是如此,否则会出现超卖(库存100,秒杀活动时库存变负数了
分布式锁原则
1、相互排斥,即任一时刻,只能有一个客户端持有锁;
2、无死锁,持有锁的客户端宕机或网络延迟下仍可获取锁;
3、有始有终,一个客户端加了锁,只能自己释放锁,当然也不能被其他客户端解锁;
4、容错性,只要大部分redis节点还存活,那么客户端就应该可以正常加锁和释放锁;
分布式锁的实现方式

大概有三种:1.基于关系型数据库,2.基于缓存,3基于zookeeper
大部分网站使用的是基于缓存的,有更好的性能,而缓存一般是以集群方式部署,保证了高可用性
总体来说,支持redis单实例、redis哨兵、redis cluster、redis master-slave等各种部署架构,都可以给你完美实现。
本次说的是基于缓存redis,使用开源 redisson 实现的分布式锁。
引入pom包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency><dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency></dependencies>
# 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();
}
}
}
更多推荐
所有评论(0)