以下工具代码,比较适用于抢单,或者提现,比如校验此人当天的提现次数,用于并发场景等等。
pom.xml添加依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
父类如下 :
/**
* @author : ws
* @date Date :
*/
public interface IExpiredCounter {
//
Long increase(String bizCode,String key, Long unit, Date expiredTime);
//redis increase 判断此刻是否达到某个key的最大值
Boolean hasIncreaseGreaterThanMaxValue(String bizCode,String key, Long unit, Date expiredTime,Long maxValue);
//redis increase 判断此刻是否达到某个key的最大值 expiredTime自动失效时间
Boolean hasIncreaseGreaterThanMaxValue(String bizCode,String key, Date expiredTime, Long maxValue);
//获取redis内 某个key 当前的单数
Long getCountValue(String bizCode,String key);
//带失效时间的increase
Long increaseBySeconds(String key, Integer senconds);
// --
Long decr(String key, Integer startNumber);
// --
Long decrByCommon(String bizCode,String key, Integer startNumber);
}
实现类:
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.text.MessageFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class RedisExpiredCounter implements IExpiredCounter {
@Autowired
private RedisTemplate redisTemplate;
@Value("${spring.redis.keyspace}")
private String keySpace;
private String counterPoolKeyFormat = ":counterpool:{0}:{1}";
private String doubleTwelveFormat = ":DOUBLE_TWELVE:{0}";
@PostConstruct
public void postConstruct() {
counterPoolKeyFormat = keySpace + counterPoolKeyFormat;
doubleTwelveFormat = keySpace + doubleTwelveFormat;
}
@Override
public Long increase(String bizCode,String key, Long unit, Date expiredTime) {
Date date = new Date();
String finalKey = MessageFormat.format(counterPoolKeyFormat,bizCode,key);
BusinessException.check(date.compareTo(expiredTime) <=0,"过期时间不能小于当前时间");
Long result = redisTemplate.opsForValue().increment(finalKey,unit);
if(redisTemplate.getExpire(finalKey) < 0) {
redisTemplate.expireAt(finalKey, expiredTime);
}
return result;
}
@Override
public Long getCountValue(String bizCode,String key) {
String finalKey = MessageFormat.format(counterPoolKeyFormat,bizCode,key);
//Object resultObj = redisTemplate.opsForValue().get(finalKey);
Object resultObj = getIncrValue(finalKey);
Long result = 0L;
if (resultObj != null && !Strings.isNullOrEmpty(resultObj.toString())) {
result = Long.valueOf(resultObj.toString());
}
return result;
}
@Override
public Long increaseBySeconds(String key, Integer senconds) {
String finalKey = MessageFormat.format(doubleTwelveFormat,key);
BoundValueOperations<String, String> stringStringBoundValueOperations = redisTemplate.boundValueOps(finalKey);
Long result = stringStringBoundValueOperations.increment();
stringStringBoundValueOperations.expire(senconds, TimeUnit.SECONDS);
return result;
}
@Override
public Long decr(String key, Integer startNumber) {
String finalKey = MessageFormat.format(doubleTwelveFormat,key);
BoundValueOperations<String, String> stringStringBoundValueOperations = redisTemplate.boundValueOps(finalKey);
Long result = stringStringBoundValueOperations.decrement(startNumber);
return result;
}
@Override
public Long decrByCommon(String bizCode,String key, Integer num) {
String finalKey = MessageFormat.format(counterPoolKeyFormat,bizCode,key);
BoundValueOperations<String, String> stringStringBoundValueOperations = redisTemplate.boundValueOps(finalKey);
Long result = stringStringBoundValueOperations.decrement(num);
return result;
}
public Object getIncrValue(final String key) {
return redisTemplate.execute((RedisCallback<Long>) connection -> {
RedisSerializer<String> serializer=redisTemplate.getStringSerializer();
byte[] rowkey=serializer.serialize(key);
byte[] rowval=connection.get(rowkey);
try {
String val=serializer.deserialize(rowval);
return Long.parseLong(val);
} catch (Exception e) {
return 0L;
}
});
}
@Override
public Boolean hasIncreaseGreaterThanMaxValue(String bizCode,String key, Long unit, Date expiredTime, Long maxValue) {
return increase(bizCode,key,unit,expiredTime).compareTo(maxValue) > 0;
}
@Override
public Boolean hasIncreaseGreaterThanMaxValue(String bizCode,String key, Date expiredTime, Long maxValue) {
return hasIncreaseGreaterThanMaxValue(bizCode,key,1L,expiredTime,maxValue);
}
}
提现相关,校验几秒内并发情况:
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class WithdrawalCacheHandler {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Value("${spring.redis.keyspace}")
private String keySpace;
private String withdrawalforsnatchHashKeyFormat = ":withdraw:{0,number,#}";
@PostConstruct
public void postConstruct() {
withdrawalforsnatchHashKeyFormat = keySpace + withdrawalforsnatchHashKeyFormat;
}
/**
* 提现接口 校验3秒内是否有并发请求 ,
* @param fcUserId
* @param money
* @return 返回true正常情况, false代表3秒内有重复请求
*/
public boolean toCheckRedisWithdrawal(Long fcUserId, BigDecimal money) {
String key = MessageFormat.format(withdrawalforsnatchHashKeyFormat, fcUserId);
Boolean hasSnatched = redisTemplate.opsForValue().setIfAbsent(key, money);
if (hasSnatched)
{
redisTemplate.expire(key,3, TimeUnit.SECONDS);
}
return hasSnatched;
}
/**
* 移除提现 占用 在finally内 解除3秒占用
* @param fcUserId
* @return
*/
public boolean removeWithdrawal(Long fcUserId) {
String key = MessageFormat.format(withdrawalforsnatchHashKeyFormat, fcUserId);
Boolean delete = redisTemplate.delete(key);
return delete;
}
}