1.在redis中放入有用户标识的key,每次用户操作时都会根据用户标识的key去匹配一下,匹配上就能操作,匹配不上就拒绝。
下面的代码是商品秒杀场景,根据用户id和商品id去生成一个标识,然后放入redis缓存中。
@Override
public String getMd5(Integer id,Integer userid){
User user = userMapper.findById(userid);
if(user == null) throw new RuntimeException("用户信息不存在!");
log.info("用户信息:[{}]",user.toString());
Stock stock = stockMapper.checkStock(id);
if(stock == null) throw new RuntimeException("商品信息不合法!");
log.info("商品信息:[{}]",stock.toString());
String hashKey = "KEY_"+userid+"_"+id;
String key = DigestUtils.md5DigestAsHex((userid+id+"!Q*jS#").getBytes());
stringRedisTemplate.opsForValue().set(hashKey,key,3600, TimeUnit.SECONDS);
log.info("Redis写入:[{}][{}]",hashKey,key);
return key;
}
用户要秒杀商品时,根据用户id和商品id去redis查找有没有对应的签名,如果有则放行,没有就抛出异常。
@Override
public int kill(Integer id, Integer userid, String md5) {
if(!stringRedisTemplate.hasKey("killphone")){
throw new RuntimeException("当前商品的抢购活动已经结束啦~~");
}
String hashKey = "KEY_"+userid+"_"+id;
String s = stringRedisTemplate.opsForValue().get(hashKey);
if(s==null)
throw new RuntimeException("没有携带验证签名,请求不合法!");
if(!s.equals(md5))
throw new RuntimeException("当前请求数据不合法,请稍后再试!");
Stock stock = checkStock(id);
updateSale(stock);
return createOrder(stock);
}
2.用redis根据用户id在一定时间范围内统计用户访问次数,超过一定次数就限制访问。
下面这个类是在redis中保存和查询用户访问次数。
@Slf4j
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public int saveUserCount(Integer userId) {
String limitKey = "LIMIT"+"_"+userId;
String limitNum = stringRedisTemplate.opsForValue().get(limitKey);
int limit = -1;
if(limitNum == null){
stringRedisTemplate.opsForValue().set(limitKey,"0",3600, TimeUnit.SECONDS);
}else{
limit = Integer.parseInt(limitNum)+1;
stringRedisTemplate.opsForValue().set(limitKey,String.valueOf(limit),3600,TimeUnit.SECONDS);
}
return limit;
}
@Override
public boolean getUserCount(Integer userId) {
String limitKey = "LIMIT"+"_"+userId;
String limitNum = stringRedisTemplate.opsForValue().get(limitKey);
if(limitNum == null){
log.error("该用户没有访问申请验证值记录,疑似异常");
return true;
}
return Integer.parseInt(limitNum) > 10;
}
}
当用户在一定时间内访问次数达到限制次数,就不让用户再去访问了。
@GetMapping("killtokenmd5limit")
public String killtokenmd5limit(Integer id,Integer userid,String md5){
System.out.println("秒杀商品的id="+id);
if(!rateLimiter.tryAcquire(3, TimeUnit.SECONDS)){
log.info("抛弃请求:抢购失败,当前秒杀活动过于火爆,请重试");
return "抛弃请求:抢购失败,当前秒杀活动过于火爆,请重试!";
}
try{
int count = userService.saveUserCount(userid);
log.info("用户截止该次的访问次数为:[{}]",count);
boolean isBanned = userService.getUserCount(userid);
if(isBanned){
log.info("购买失败,超过频率限制!");
return "购买失败,超过频率限制!";
}
int orderId = orderService.kill(id,userid,md5);
return "秒杀成功,订单id为:"+String.valueOf(orderId);
}catch(Exception e){
e.printStackTrace();
return e.getMessage();
}
}
|