@Component
public class LimiterUtil {
@Resource
private RedisTemplate<String, String> redisTemplate;
/**
* 固定窗口限流算法
*
* @return true 限流 false 放行
*/
public boolean fixedWindow(String key, int count) {
long countCache = redisTemplate.opsForValue().increment(key);
return countCache > count;
}
/**
* intervalTime 时间内 最多只能访问5次
*
* @param key 缓存key
* @param currentTime 当前时间 new Date().getTime();
* @param intervalTime 有效期
* @param count 限流次数
* @return true 限流 false 放行
*/
public boolean slidingWindow(String key, Long currentTime, Long intervalTime, int count) {
//发送次数+1
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), currentTime);
// intervalTime是限流的时间
int countCache = Objects.requireNonNull(redisTemplate.opsForZSet().rangeByScore(key, currentTime - intervalTime, currentTime)).size();
return countCache > count;
}
}
/**
*发送短信单位时间内次数限制采用的是slidingWindow()方法的参数。
*/
public CommonResult<Boolean> sendCode(String phone, String codeCachePre, Integer msgType) {
if (StringUtil.isEmpty(phone)) {
return CommonResult.failed(ResultCode.VALIDATE_FAILED);
}
ParamValidate.isNull(phone, "phone参数为空");
//同一个手机号码 发送次数限流
//同一个手机号每秒 最多只能发5次
boolean limit = limiterUtil.slidingWindow(RedisConstantPre.MESSAGE_LIMIT_KEY_PRE + phone, (new Date()).getTime(), 60000L, 5);
if (limit) {
return CommonResult.failed(ResultCode.SEND_MESSAGE_LIMIT);
}
CommonResult<MessageTemplateDto> commonResult = messageTemplateFeignClient.queryByMessageType(msgType);
if (commonResult.getCode() != ResultCode.SUCCESS.getCode()) {
log.error("短信模板不存在,tye={}", msgType);
return CommonResult.failed(ResultCode.VALIDATE_FAILED);
}
MessageTemplateDto messageTemplateDto = commonResult.getData();
//生成随机验证码
String code = RandomUtil.randomNumbers(6);
log.info("登录发送验证码:{}", code);
//发送验证码
String content = messageTemplateDto.getContent();
//验证码占位符
String newContent = content.replace(messageTemplateDto.getContentParam(), code);
//调用MQ 异步发送短信验证码
phoneMessageProducer.sendPhoneMessage(phone, newContent);
//存到redis中 设置有效期 60秒
//60秒后需要重现发送
redisConfig.set(codeCachePre + phone, code, 60);
return CommonResult.success(Boolean.TRUE);
}