Redisson
- 前言
- 一、依赖
- 二、配置文件
- 三、锁的使用
- 读写锁
- 信号量
- 闭锁
- 四、分布式秒杀
- 五、redis锁 单机版可用,分布式用Redisson
前言
Redisson是一个在Redis的基础上实现的Java驻内存数据网格
一、依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.4</version>
</dependency>
二、配置文件
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 ThreadLocal<String> threadLocal = 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<Long>(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 "库存不足,该商品已抢购完!";
}