由于业务系统服务接口被竞对高频爬取,我们需要上线一个防刷限流功能。场景要求如下:根据登陆ip,限制1分钟最多10次服务请求,须支持单机并发场景,请使用java代码原生实现(勿使用中间件redis、计数器之类方案)。
// 请完善如下模板代码
public class Solution {
//不限流返回ture,限流返回false
public boolean access(String ip) {
// @todo
return false;
}
}
package com.it.juxiang.demo.bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 模拟同一用户快速多次登录(请求),通过(UserLoginLimiter)限制
*
*/
@Controller
public class UserLoginTimeController {
@Autowired
private UserLoginLimiter userLoginLimiter;
@PostMapping("/bool")
@ResponseBody
public String test() {
String ip="192.168.198.10";
if (userLoginLimiter.check(ip)) {
System.out.println(ip + ":访问成功:true...........!");
return (ip + ":访问成功:true!");
} else {
System.out.println(ip +":访问失败:false!");
return (ip + ":访问失败:false!");
}
}
}
package com.it.juxiang.demo.bool;
import org.springframework.stereotype.Component;
import java.util.LinkedList;
/**
* 用户登录记录限制类
* 限制在一定时长(thresholdInMillisecond)内最多登录(maxLoginTimes)次
*/
@Component
public class UserLoginLimiter {
//默认限制器,限制在60秒之内最多访问10次
private int maxLoginTimes=10; //一定时间(thresholdInMillisecond值)内的最多登录次数
private int thresholdInMillisecond=60000; //时长
private LinkedList<LoginRecord> loginRecordList=new LinkedList<LoginRecord>(); //最近的登录队列
/**
* 检查登录记录,
* 对在(thresholdInMillisecond)时长内超过(maxloginTimes)次的登录记录返回false,否则返回true;
* @param ip
* @return
*/
public boolean check(String ip) {
//检查设置的值,对0及负数表示不限制登录
if (maxLoginTimes <= 0 || thresholdInMillisecond <= 0) return true;
LoginRecord loginRecord=new LoginRecord(ip,System.currentTimeMillis());
//队列长度未到maxLoginTimes的不用限制
if (loginRecordList.size() < maxLoginTimes) {
loginRecordList.addLast(loginRecord);
return true;
}
//队列长度达到maxLoginTimes的,清理和当前记录比较超过thresholdInMillisecond值的记录
LoginRecord firstLR = loginRecordList.getFirst();
while (loginRecordList.size() > 0 && firstLR.getTime() + thresholdInMillisecond < loginRecord.getTime()) {
loginRecordList.removeFirst();
if (loginRecordList.size() > 0) {
firstLR = loginRecordList.getFirst();
} else {
break;
}
}
//检查队列是否有空间添加新的记录
if (loginRecordList.size() < maxLoginTimes) {
loginRecordList.addLast(loginRecord);
return true;
} else {
return false; //队列中的记录与当前登录记录发生在thresholdInMillisecond内,拒绝
}
}
}
package com.it.juxiang.demo.bool;
/**
* 用户登录记录类
*/
public class LoginRecord {
private String ip;
private long time;
public LoginRecord(String ip, long time) {
this.ip = ip;
this.time = time;
}
public String getIp() {
return ip;
}
public long getTime() {
return time;
}
@Override
public String toString() {
return "LoginRecord{" +
"ip='" + ip + '\'' +
", time=" + time +
'}';
}
}
推荐一个软件,可以方便测试多线程下的运行:
Apache JMeter WEB