springboot集成redission及分布式锁的使用
1、引入jar包
org.redisson redisson 3.13.4
2、增加Configuration类
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private String port; @Value("${spring.redis.password}") private String password; @Bean public RedissonClient getRedisson() { Config config = new Config(); config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password); return Redisson.create(config); } }
3、使用redission分布式锁
@Autowired private RedissonClient redissonClient;
//方法区 String key = "aa:bb:cc:01"; RLock rLock =redissonClient.getLock(key); try{
// 尝试加锁,最多等待1秒,上锁以后10秒自动解锁
// 没有Watch Dog ,10s后自动释放 boolean res = rLock.tryLock(1,10, TimeUnit.SECONDS); if(!res){ return new GeneralVO<>(400, "请勿重复提交", false); } }finally{ rLock.unlock(); }
private void redissonDoc() throws InterruptedException { //1. 普通的可重入锁 RLock lock = redissonClient.getLock("generalLock"); // 拿锁失败时会不停的重试 // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s lock.lock(); // 尝试拿锁10s后停止重试,返回false // 具有Watch Dog 自动延期机制 默认续30s boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 拿锁失败时会不停的重试 // 没有Watch Dog ,10s后自动释放 lock.lock(10, TimeUnit.SECONDS); // 尝试拿锁100s后停止重试,返回false // 没有Watch Dog ,10s后自动释放 boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); //2. 公平锁 保证 Redisson 客户端线程将以其请求的顺序获得锁 RLock fairLock = redissonClient.getFairLock("fairLock"); //3. 读写锁 没错与JDK中ReentrantLock的读写锁效果一样 RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock"); readWriteLock.readLock().lock(); readWriteLock.writeLock().lock(); }
Springboot整合Redisson 锁
Redisson是一个在Redis的基础上实现的Java驻内存数据网格
一、依赖
org.redisson redisson 3.15.4
二、配置文件
spring: redis: database: 7 host: 116.62.178.11 port: 6379 password: 1234qwer # spring-boot 1.0默认 jedis; spring-boot2.0 默认lettuce ,lettuce线程安全 lettuce: pool: # 连接池中的最大空闲连接 默认8 max-idle: 8 # 连接池中的最小空闲连接 默认0 min-idle: 500 # 连接池最大连接数 默认8 ,负数表示没有限制 max-active: 2000 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1 max-wait: -1 cache: type: redis
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.password}") private String password; @Bean(destroyMethod = "shutdown") RedissonClient redissonClient() throws IOException { Config config = new Config(); config.useSingleServer() .setPassword(password) .setAddress("redis://" + host + ":" + port).setDatabase(7); return Redisson.create(config); } }
三、锁的使用
读写锁
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 读写锁 总结 * * 读锁又叫共享锁 * 写锁又叫排他锁(互斥锁) * 读 + 读 相当于无锁,并发读,同时加锁成功 * 写 + 写 阻塞状态 * 写 + 读 等待写锁释放 * 读 + 写 等待读锁完,才写, */ public String writeValue() { String str = ""; RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock"); RLock rLock = readWriteLock.writeLock(); try { rLock.lock(); str = UUID.randomUUID().toString(); redisTemplate.opsForValue().set("uuid", str); Thread.sleep(30000); } catch (Exception e) { } finally { rLock.unlock(); } return str; } /** * 读锁 * * @return */ public String readValue() { String str = ""; RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock"); RLock rLock = readWriteLock.readLock(); rLock.lock(); str = (String) redisTemplate.opsForValue().get("uuid"); rLock.unlock(); return str; } }
信号量
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 信号量 * * @return */ //停车方法 @GetMapping("/park") public String park() throws InterruptedException { //这里是获取信号量的值,这个信号量的name一定要与你初始化的一致 RSemaphore park = redissonClient.getSemaphore("park"); //这里会将信号量里面的值-1,如果为0则一直等待,直到信号量>0 park.acquire(); //tryAcquire为非阻塞式等待 //park.tryAcquire(); return "ok"; } public String go() throws InterruptedException { //这里是获取信号量的值,这个信号量的name一定要与你初始化的一致 RSemaphore park = redissonClient.getSemaphore("park"); //这里会将信号量里面的值+1,也就是释放信号量 park.release(); return "ok"; } }
闭锁
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 闭锁,限流 * * @return * @throws InterruptedException */ //锁门 public String lockdoor() throws InterruptedException { RCountDownLatch door = redissonClient.getCountDownLatch("door"); //设置一个班级有20个同学 door.trySetCount(20); //需要等到20个同学全部离开,才锁门 door.await(); return "锁门了"; } public String leave(Long id) throws InterruptedException { RCountDownLatch door = redissonClient.getCountDownLatch("door"); //表示一个同学离开 door.countDown(); return "" + id + "号同学离开了"; } }
四、分布式秒杀
秒杀流程:
@Service @Slf4j public class DistributedSecKillBiz { @Autowired private RedisTemplate redisTemplate; @Autowired private RedissonClient redissonClient; /** * 分布式锁。唯一缺点 枷锁失效时间 * 枷锁院子操作, * 解锁,删除锁也是原子操作 瑕疵没有续命 * * @return */ public String doKill() { String lock = UUID.randomUUID().toString(); String goodsId = "10054"; boolean flag = redisTemplate.opsForValue().setIfAbsent(goodsId, lock, 30, TimeUnit.SECONDS); if (flag) { // 获取锁成功 try { Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣减库存成功,还剩:" + stock); } return "库存不足,该商品已抢购完!"; } catch (Exception e) { } finally { String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(goodsId), lock); } } return doKill(); } /** * 整合 redission * @return */ public String doKillDistributed() { String goodsId = "10054"; RLock lock = redissonClient.getLock(upActivityKey() + SecKillConstant.LOCK + goodsId); // 获取锁成功 try { //1 阻塞式等待,默认30秒时间 //2 自动续期,如果业务超长,续上新的30秒,不用担心过期时间,锁自动删除掉 //3 枷锁的业务运行完成,就不会给当前的锁自动续期,即使没有手动释放锁也会,30秒自动释放 // lock.lock(30, TimeUnit.SECONDS); //不会自动续期需要注意 lock.lock(); Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣减库存成功,还剩:" + stock); } return "库存不足,该商品已抢购完!"; } catch (Exception e) { } finally { lock.unlock(); } return "fail"; } /** * 获取活动 * * @return */ public ActivityBo upActivity() { return new ActivityBo("七夕活动", "SEVEN_ACTIVITY", new Date(), new Date()); } /** * 活动公共key * * @return */ public String upActivityKey() { return SecKillConstant.SEC_KILL + upActivity().getActivityKey() + ":"; } }
五、redis锁 单机版可用,分布式用Redisson
package com.yang.yimall.seckill.app.seckill.biz; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * redis 锁 集群有瑕疵 不能 续命 */ @Service public class RedisLock { @Autowired private RedisTemplate redisTemplate; private String lockName = "lockName"; private ThreadLocalthreadLocal = new ThreadLocal<>(); public void lock(String lockName) { if (tryLock(lockName)) { return; } lock(lockName); } public void lock() { if (tryLock(lockName)) { return; } lock(); } /** * 添加key 并且设置过期时间 原子操作 * * @param lockName * @return */ public boolean tryLock(String lockName) { String uuid = UUID.randomUUID().toString(); threadLocal.set(uuid); return redisTemplate.opsForValue().setIfAbsent(lockName, uuid, 30, TimeUnit.SECONDS); } /** * 如果查询有key,就删除, 原子操作 */ public void unlock() { String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript (script, Long.class), Collections.singletonList(lockName), threadLocal.get()); } }
使用
public String doKillUp() { String goodsId = "10054"; redisLock.lock(goodsId); // 获取锁成功 try { Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣减库存成功,还剩:" + stock); } return "库存不足,该商品已抢购完!"; } catch (Exception e) { } finally { redisLock.unlock(); } return "库存不足,该商品已抢购完!"; }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。