maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
控制余量
private RedisTemplate<Object, Object> redisTemplate;
ListOperations<Object, Object> listRedis = redisTemplate.opsForList();
//采用Redis的list类型处理该业务场景,一开始通过leftPushAll方法放进值都为1总数量n个的value,key为所需控制余量的对象;
//设置总量
String[] strs = new String[n];
for (int i = 0; i < n; i++) {
strs[i] = "1";
}
listRedis.leftPushAll(key,strs);
//判断目标是否还有余量
if(!Strings.isNullOrEmpty(listRedis.rightPop(key))){
//业务代码
...
if(
//业务正常,没出现问题
...
){
//业务代码
...
}else{
//业务出现问题,回滚库存
listRedis.leftPushAll(key, 1);
}
}
排行榜
private RedisTemplate<Object, Object> redisTemplate;
ZSetOperations<Object, Object> zSetRedis = redisTemplate.opsForZSet();
//sorted set类型的add方法加入数据
zSetRedis.add(key,value,score);
//reverseRank获取排名
zSetRedis.reverseRank(key,value)+1;
//获取前100名的排行榜
Set<Object> zrevrange = zSetRedis.reverseRange(key, 0L, 99L);
if(zrevrange.isEmpty()){
return;
}
List<Object> list = new ArrayList<>(zrevrange);
List<T> tlist = new ArrayList<>();
Iterator iterator=list.iterator();
while(iterator.hasNext()){
Long teamId = Long.parseLong((String) iterator.next());
T t = new T();
//业务代码,数据处理存储排名信息
...
tlist.add(t);
}
简易的分布式锁
分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
「互斥性」: 任意时刻,只有一个客户端能持有锁。 「锁超时释放」:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。
「可重入性」:一个线程如果获取了锁之后,可以再次对其请求加锁。
「高性能和高可用」:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。
「安全性」:锁只能被持有的客户端删除,不能被其他客户端删除
private RedisTemplate<Object, Object> redisTemplate;
ValueOperations<Object, Object> stringOps = redisTemplate.opsForValue();
//setnx如果为空就set值,并返回1,如果存在(不为空)不进行操作,并返回0,调用String类型的setIfAbsent方法。注:最好封装下
if(!stringRedis.setIfAbsent(key, "1", seconds, TimeUnit.SECONDS)){
//该资源在被占用,作相应的业务代码处理,eg. log.info("操作过快/请求重复");
...
}
但上诉方案存在2个问题,①线程a的锁过期释放了,但业务代码未结束,线程b就进来;②线程a的锁过期释放了,然后b线程进来,此时a业务代码结束了开始释放锁,但此时这个锁是b线程的锁
对于①有想过延长过期时间去解决或者开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放;对于②曾想过value值设置一个标记当前线程唯一的随机数,校验再进行删除避免出现误删的情况,但判断行为和释放锁行为不是同一个原子性操作,只能采用lua脚本实现;
还有一种方案是多机实现的分布式锁Redlock+Redisson
锁重试场景
final String lock = MessageFormat.format(Constant.LOCK, Id);
int count = 0;
while(!stringRedis.setIfAbsent(key, "1", seconds, TimeUnit.SECONDS)){
try {
Thread.sleep(200L);
}catch (Exception e) {
Thread.currentThread().interrupt();
}
count++;
if(count>10){
return null;
}
}
附:访问链接的重试
Boolean s = false;
int i = 0;
while (true){
s = this.访问第三方接口();
if (s || i > 10){
break;
}
++i;
}