我是靠谱客的博主 冷静高山,最近开发中收集的这篇文章主要介绍Redisson—分布式锁框架一、 Redisson介绍二、 Redisson工作原理三、 Redisson分布式锁特点四、 Redisson使用五、 Redisson使用实例六、 Redisson使用扩展七、 lua脚本——实现分布式线程原子性,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、 Redisson介绍

Redisson在基于NIO的Netty框架上,充分的利用了Redis键值数据库提供的⼀系列优势,为使用者提供了⼀系列具有分布式特性的常用⼯具类。
一款基于Redis+看门狗机制的分布式锁框架

二、 Redisson工作原理

在这里插入图片描述

  • 看门狗机制
    在这里插入图片描述

三、 Redisson分布式锁特点

1、互斥性
和我们本地锁⼀样互斥性是最基本,但是分布式锁需要保证在不同节点的不同线程的互斥。
2、可重⼊性
同⼀个节点上的同⼀个线程如果获取了锁之后那么也可以再次获取这个锁。
3、锁超时
和本地锁⼀样⽀持锁超时,加锁成功之后设置超时时间,以防⽌线程故障导致不释放锁,防⽌死锁。
4、⾼效,⾼可⽤
加锁和解锁需要⾼效,同时也需要保证⾼可⽤防⽌分布式锁失效,可以增加降级。

redission是基于redis的,redis的故障就会导致redission锁的故障,因此redission⽀持单节点redis、reids主从、reids集群
5、⽀持阻塞和⾮阻塞
和 ReentrantLock ⼀样⽀持 lock 和 trylock 以及 tryLock(long timeOut)

四、 Redisson使用

  • 获取锁——公平锁和非公平锁
//获取公平锁——按照线程的先后顺序获取锁
RLock lock = redissonClient.getFairLock(skuId);
//获取⾮公平锁——多个线程随机获取锁
RLock lock = redissonClient.getLock(skuId);
  • 加锁——阻塞锁和⾮阻塞锁
//阻塞锁——不断尝试获取锁,直到获取到锁为⽌(加锁成功后,默认因线程故障超时时间为30s后释放锁;开启看⻔狗,剩5s延⻓过期时间)
lock.lock();
//阻塞锁(如果加锁成功之后,设置⾃定义20s的超时时间)
lock.lock(20,TimeUnit.SECONDS);
//⾮阻塞锁——在指定时间内不断获取锁(3秒内不断尝试获取线程加锁,默认因线程故障超时时间为30s后释放锁)
boolean b = lock.tryLock(3,TimeUnit.SECONDS);
//⾮阻塞锁(设置等待时间为3s;如果加锁成功设置⾃定义超时时间为20s)
boolean b = lock.tryLock(3,20,TimeUnit.SECONDS);
  • 释放锁
lock.unlock();
  • 应用示例
//公平⾮阻塞锁
RLock lock = redissonClient.getFairLock(skuId);
boolean b = lock.tryLock(3,20,TimeUnit.SECONDS);

五、 Redisson使用实例

  • 添加依赖
<dependency>
	 <groupId>org.redisson</groupId>
	 <artifactId>redisson</artifactId>
	 <version>3.12.0</version>
</dependency>
  • application.yml(单体Redis)
redisson:
	addr:
 		singleAddr:
			host: redis://47.96.11.185:6370
 			password: 12345678
 			database: 0
  • 配置RedissonConfig
@Configuration
public class RedissonConfig {

	 @Value("${redisson.addr.singleAddr.host}")
	 private String host;
	 @Value("${redisson.addr.singleAddr.password}")
	 private String password;
	 @Value("${redisson.addr.singleAddr.database}")
	 private int database;
	 
	 @Bean
	 public RedissonClient redissonClient(){
	 Config config = new Config();
	 
	 config.useSingleServer().setAddress(host).setPassword(password).setDatabase(database);
	 return Redisson.create(config);
		 }
}
  • 伪代码
HashMap map = null;
加锁
try{
	 if(isLock){
		 校验库存
		 if(库存充⾜){
			 保存订单
			 保存快照
			 修改库存
			 删除购物⻋
			 map = new HashMap();
			 ...
		 }
	 }
}catch(Exception e){
 e.printStackTrace();
}finally{
 释放锁
}
return map;
  • 在保存订单业务实现中Redisson分布式分布式锁
/**
* 保存订单业务
*/
@Autowired
private stringRedisTemplate stringRedisTemplate;
@Autowired
private Redissonclient redissonclient;

@Transactional
public Map<String, String> addOrder(String cids, Orders order) throws SQLException {

	 Map<String, String> map = null;
	 //1.校验库存:根据cids查询当前订单中关联的购物⻋记录详情(包括库存)
	 String[] arr = cids.split(",");
	 List<Integer> cidsList = new ArrayList<>();
	 
	 for (int i = 0; i < arr.length; i++) {
	 cidsList.add(Integer.parseInt(arr[i]));
	 }
	 
	 //根据⽤户在购物⻋列表中选择的购物⻋记录的id 查询到对应的购物⻋记录
	 List<ShoppingCartVO> list = shoppingCartMapper.selectShopcartByCids(cidsList);
	 
	 //加锁
	 boolean isLock = true;
	 String[] skuIds = new String[list.size()]; 
	 Map<String, RLock> locks = new HashMap<>(); //⽤于存放当前订单的锁
	 for (int i = 0; i < list.size(); i++) {
	 String skuId = list.get(i).getSkuId();
	 boolean b = false;
	 
		 try {
	 	  //获取公平锁(按照线程的先后顺序获取锁)
		 RLock lock = redissonClient.getLock(skuId);
		 //加⾮阻塞锁(10秒内不断尝试获取线程加锁,如果加锁成功设置⾃定义线程出现故障无响应时间为20s删除锁)
		 b = lock.tryLock(10, 3, TimeUnit.SECONDS);
		 
		 if (b) {
			 skuIds[i] = skuId;
			 locks.put(skuId, lock);
			 	}
		 	} catch (InterruptedException e) {
			 e.printStackTrace();
		 }
		 isLock = isLock & b;
	 }
		 //如果isLock为true,表示“加锁”成功
		try {
			 if (isLock){
			 //1.检验库存
			 boolean f = true;
			 String untitled = "";
			 list = shoppingCartMapper.selectShopcartByCids(cidsList);
			 for (ShoppingCartVO sc : list) {
			 if (Integer.parseInt(sc.getCartNum()) > sc.getSkuStock()) {
			 f = false;
			 }
			 untitled = untitled + sc.getProductName() + ",";
			 }
			 if (f) {
			 //如果库存充⾜,则进⾏下订单操作
			 logger.info("product stock is OK...");
			 //2.保存订单
			 order.setUntitled(untitled);
			 order.setCreateTime(new Date());
			 order.setStatus("1");
			 //⽣成订单编号
			 String orderId = UUID.randomUUID().toString().replace("-", "");
			 order.setOrderId(orderId);
			 int i = ordersMapper.insert(order);
			 //3.⽣成商品快照
			 for (ShoppingCartVO sc : list) {
			 int cnum = Integer.parseInt(sc.getCartNum());
			 String itemId = System.currentTimeMillis() + "" + (new
			 Random().nextInt(89999) + 10000);
			 OrderItem orderItem = new OrderItem(itemId, orderId, sc.getProductId(),
			sc.getProductName(), sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new
			BigDecimal(sc.getSellPrice()), cnum, new BigDecimal(sc.getSellPrice() * cnum), new Date(),new Date(), 0);
			 orderItemMapper.insert(orderItem);
			 //增加商品销量
			 }
			 //4.扣减库存:根据套餐ID修改套餐库存量
			 for (ShoppingCartVO sc : list) {
			 String skuId = sc.getSkuId();
			 int newStock = sc.getSkuStock() - Integer.parseInt(sc.getCartNum());
			 ProductSku productSku = new ProductSku();
			 productSku.setSkuId(skuId);
			 productSku.setStock(newStock);
			 productSkuMapper.updateByPrimaryKeySelective(productSku);
			 }
			 //5.删除购物⻋:当购物⻋中的记录购买成功之后,购物⻋中对应做删除操作
			 for (int cid : cidsList) {
			 shoppingCartMapper.deleteByPrimaryKey(cid);
			 }
			 map = new HashMap<>();
			 logger.info("add order finished...");
			 map.put("orderId", orderId);
			 map.put("productNames", untitled);
			 }
			 }
		 }catch (Exception e){
		 e.printStackTrace();
		 }finally {
			 for (int i = 0; i < skuIds.length; i++) {
			 String skuId = skuIds[i];
			 if (skuId != null && !"".equals(skuId)) {
			 //释放锁
			 locks.get(skuId).unlock();
			 System.out.println("-----------------------unlock");
			 	}
			 }
		 }
		 return map; 
 }

六、 Redisson使用扩展

  • application.yml—Redisson集群连接
redisson:
	addr:
 		cluster:
			host:  redis://47.96.11.185:6370,...,redis://47.96.11.185:6373
 			password: 12345678
 		
  • 配置RedissonConfig
@Configuration
public class RedissonConfig {

	@Value("${redisson.addr.cluster.hosts}")
 	private String hosts;
 	@Value("${redisson.addr.cluster.password}")
	private String password;
	 /**
	 * 集群模式
	 * @return
	 */
	 @Bean
	 public RedissonClient redissonClient(){
	 Config config = new Config();
	 config.useClusterServers().addNodeAddress(hosts.split("[,]"))
			 .setPassword(password)
			 .setScanInterval(2000)
			 .setMasterConnectionPoolSize(10000)
			 .setSlaveConnectionPoolSize(10000);
	 return Redisson.create(config);
	}
}
  • application.yml—Redisson主从连接
redisson:
	addr:
 		masterAndSlave:
			masterhost: redis://47.96.11.185:6370
			slavehosts: redis://47.96.11.185:6371,redis://47.96.11.185:6372
			password: 12345678
	 		database: 0
  • 配置RedissonConfig
@Configuration
public class RedissonConfig {

	@Value("${redisson.addr.masterAndSlave.masterhost}")
	 private String masterhost;
	 @Value("${redisson.addr.masterAndSlave.slavehosts}")
	 private String slavehosts;
	 @Value("${redisson.addr.masterAndSlave.password}")
	 private String password;
	 @Value("${redisson.addr.masterAndSlave.database}")
	 private int database;
	 
	 /**
	 * 主从模式
	 * @return
	 */
	 @Bean
	 public RedissonClient redissonClient(){
	 Config config = new Config();
	 config.useMasterSlaveServers()
			 .setMasterAddress(masterhost)
			 .addSlaveAddress(slavehosts.split("[,]"))
			 .setPassword(password)
			 .setDatabase(database)
			 .setMasterConnectionPoolSize(10000)
			 .setSlaveConnectionPoolSize(10000);
	 return Redisson.create(config);
	}
}

七、 lua脚本——实现分布式线程原子性

  • 在resources⽬录下创建unlock.lua,编辑脚本
if redis.call("get",KEYS[1]) == ARGV[1] then
	 return redis.call("del",KEYS[1])
else
	 return 0
end
  • 配置Bean加载lua脚本
@Bean
public DefaultRedisScript<List> defaultRedisScript(){
	 DefaultRedisScript<List> defaultRedisScript = new DefaultRedisScript<>();
	 defaultRedisScript.setResultType(List.class);
	 defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));
	 return defaultRedisScript; 
	 }
  • 执行lua脚本解锁
@Autowired
private DefaultRedisScript defaultRedisScript;

//生成锁的key
String lockKey = aid+"LDOK";
//生成锁的唯一id
String lockId =UUID.randomUUIDT().toString();
//添加锁——以lockKey 为key lockId 为value 过期时间为30s
Boolen flag = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,lockId,30,Tinedpit SECONDS);


//执⾏lua脚本——释放锁
List<String> keys = new ArrayList<>();
keys.add(lockKey);
List rs = stringRedisTemplate.execute(defaultRedisScript,keys,lockId);
System.out.println(rs.get(0));

最后

以上就是冷静高山为你收集整理的Redisson—分布式锁框架一、 Redisson介绍二、 Redisson工作原理三、 Redisson分布式锁特点四、 Redisson使用五、 Redisson使用实例六、 Redisson使用扩展七、 lua脚本——实现分布式线程原子性的全部内容,希望文章能够帮你解决Redisson—分布式锁框架一、 Redisson介绍二、 Redisson工作原理三、 Redisson分布式锁特点四、 Redisson使用五、 Redisson使用实例六、 Redisson使用扩展七、 lua脚本——实现分布式线程原子性所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部