因为这个涉及到公司安全漏洞,确实很大隐患,情况描述就抽象一些
情景描述:我们项目中一个很重要的接口,条件限制全部通过是操作redis的读写,与服务器上配置表数据进行判断比较。
因为这个接口很重要,也很敏感。刚好最近在摸索JMEMTER的使用,心想刚好可以测试下,并发的访问下我这个接口,我们的限制能够生效嘛?(这个疑问之前就想过,我们的redis读写操作,不具有原子性。但是我们老大告诉我,Redis的操作很快,不会出现限制不住的问题)
2千个并发,100~200多个请求都没有限制住。
解决思路
我想到三种方式
- 接口的最前面,用ConcurrentHashMap 做一个计时器。同一用户不能在多少秒内重复访问
- 拦截器的方式
- 过滤器的方式
因为我们的限制条件都在Redis上,在线上环境已经出现过几次问题,导致我们的限制也没有生效。我思考解决问题的方式时,就避开了再用Redis。
结合我项目的结构,最后我选择了使用过滤器的方式。
过滤器+ConcurrentHashMap 接口限流
我讲过滤器的代码贴上
**
* @Author yjc
* @Date 2020/6/6 10:01
*/
public class MyFilter implements Filter {
protected Logger log = Logger.getLogger(MyFilter.class);
private static final long SECOND = 1000;
/**
* 用户访问的时间
*/
private ConcurrentHashMap<String,Long> userVisitTime = new ConcurrentHashMap<>(16);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
HttpServletRequest request = (HttpServletRequest) servletRequest;
/**
* 用户的唯一标识
*/
String key = request.getParameter("userId");
try{
Long lastTime = userVisitTime.get(key);
if (lastTime != null && lastTime+SECOND <= System.currentTimeMillis()){
userVisitTime.put(key, System.currentTimeMillis());
filterChain.doFilter(servletRequest,servletResponse);
}else if (lastTime == null){
userVisitTime.put(key, System.currentTimeMillis());
filterChain.doFilter(servletRequest,servletResponse);
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
@Override
public void destroy() {
}
}
同时还需要再web.xml添加上,我只做了这个重要接口的限制
<filter>
<filter-name>myFileter</filter-name>
<filter-class>com.mini.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFileter</filter-name>
<url-pattern>/data/xx.action</url-pattern>
</filter-mapping>
下面给大家测试一下,同样的2000个并发,我接口的开始打印了消息,下面是测试结果
设计缺陷
当然这样设计也有很大的问题,如果用户量达到100w,这个ConcurrentHashMap内存占用会不断增大。
还请各位大佬,提出批评指正!