系统运维系列 之java控制api接口请求次数

Posted 琅晓琳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了系统运维系列 之java控制api接口请求次数相关的知识,希望对你有一定的参考价值。

1 前言:
本篇博客运用的背景是:控制并发数量;防止恶意恶意侵占资源,导致正常的请求无法响应;用于接口限制的其它用途。

2 使用到的技术:
2.1 Java过滤器:
过滤器是处于客户端与服务器资源文件之间的一道过滤网,在访问资源文件之前,通过一系列的过滤器对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改。也可以对响应进行过滤,拦截或修改响应。
简单理解就是过滤器是客户端请求服务器的一种规则设定,符合规则的会递交给下一个过滤器过滤,或者直接可以访问资源文件;如果不符合规则,就会被过滤器过滤掉,不再进行资源文件的访问允许。
2.2 配置原则:
(1)新建一个class,实现接口Filter(注意:是javax.servlet中的Filter);
(2)重写过滤器的doFilter(request,response,chain)方法。另外两个init()、destroy()方法一般不需要重写。在doFilter方法中进行过滤操作;
(3)在web.xml中配置过滤器:

<filter>
    <filter-name>过滤器名称</filter-name>
    <filter-class>过滤器所在的类名</filter-class>   
</filter>
<filter-mapping>
  <filter-name>过滤器名称</filter-name>
   <url-pattern>过滤规则设定A</url-pattern>
</filter-mapping>
*过滤规则设定A:
作用所有web资源:<url—pattern>/*</url-pattern>
作用于某一文件夹下所有文件:<url-pattern>/admin/*</url-pattern>
作用于某一种类型的文件:<url—pattern>*.action</url-pattern>
作用于某一文件夹下某一类型文件:<url-pattern>/admin/*.jsp</url-pattern>
*过滤器不起作用:
排查自己写的过滤器是否在struts过滤器前面,如果在struts过滤器后面建议提前试试;
如果一个过滤器需要过滤多种文件,需要配置多个<filter-mapping>,不要一个<filter-mapping>里面配置多个文件,然后文件和文件之间用逗号隔开,这种一般会不起作用。如:
<filter>
    <filter-name>过滤器名称</filter-name>
    <filter-class>过滤器所在的类名</filter-class>   
</filter>
<filter-mapping>
  <filter-name>过滤器名称</filter-name>
   <url-pattern>过滤规则设定A</url-pattern>
</filter-mapping>
<filter-mapping>
  <filter-name>过滤器名称</filter-name>
   <url-pattern>过滤规则设定B</url-pattern>
</filter-mapping>

3 控制api接口请求次数思路加部分代码:
在web.xml中增加一个过滤器filter,然后在过滤器filter设置过滤原则,首先统计设置时间(如1分钟以内)的请求次数,当超过请求次数时触发拦截机制,直接返回异常信息,不再进行资源访问。以下思路和代码参考资料1中的资料内容,感谢【半知半行】,其中B、C部分与【半知半行】代码略有不同,大家各自选取利用。

//A部分:
package com.filter;
public class AccessStatus {
	//初次请求时间
    private long lastTime;
    //请求计数
    private  int count;
    //是否已经处理
    private boolean isProcessed;
    public AccessStatus(){
        this.lastTime = System.currentTimeMillis();
        this.count = 1;
    }
    //getter/setter
	public long getLastTime() {
		return lastTime;
	}
	public void setLastTime(long lastTime) {
		this.lastTime = lastTime;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	public boolean isProcessed() {
		return isProcessed;
	}
	public void setProcessed(boolean isProcessed) {
		this.isProcessed = isProcessed;
	}
}

//B部分:需要小改动一个地方才可以正常运行,可以留言一起研究
package com.filter;
import java.util.LinkedHashMap;
import javax.servlet.http.HttpServletRequest;
public class FrequentAccessController {
	private static final LinkedHashMap<String, AccessStatus> accessMap = new LinkedHashMap<>();
	// 限制时间 = 1分钟
	private int timeInterval = 60000;
	// 开放时间 = 1分钟
	private int waitTime = 60000;
	// 在限制时间内的请求的次数 = 10
	private int maxReq = 10;
	public boolean isFrequentAccess(HttpServletRequest request) {
		// 用url表示
		String key = request.getRequestURL().toString();
		AccessStatus status = new AccessStatus();
		long currTime = System.currentTimeMillis();
		if (accessMap.containsKey(key)) {
			status = accessMap.get(key);
			if (currTime - status.getLastTime() < timeInterval) {
				int count = status.getCount();
				if (count > maxReq) {
					return true;
				} else {
					count = count + 1;
					status.setCount(count);
				}
			} else {
				if (currTime - status.getLastTime() > waitTime) {
					accessMap.remove(key);
				}
			}
		} else {
			accessMap.put(key, new AccessStatus());
		}
		return false;
	}
	public static LinkedHashMap<String, AccessStatus> getAccessMap() {
		return accessMap;
	}
	public int getMaxReq() {
		return maxReq;
	}
	public void setMaxReq(int maxReq) {
		this.maxReq = maxReq;
	}
	public int getTimeInterval() {
		return timeInterval;
	}
	public void setTimeInterval(int timeInterval) {
		this.timeInterval = timeInterval;
	}
	public int getWaitTime() {
		return waitTime;
	}
	public void setWaitTime(int waitTime) {
		this.waitTime = waitTime;
	}
}

//C部分:
package com.filter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
public class FrequentAccessControlFilter implements Filter {
	private final static FrequentAccessController frequentAccessControl = new FrequentAccessController();
	// 拦截的请求方式  只拦截post请求(想拦截其他的请求方式可以自行添加)
    private final static String methodName="POST";
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
	}
	@Override
	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (frequentAccessControl.isFrequentAccess(request) && methodName.toUpperCase().equals(request.getMethod())) {
        	String result = "";
        	Map<String,Object> map = new HashMap<String, Object>();
        	response.setCharacterEncoding("UTF-8");
        	PrintWriter out = response.getWriter();
        	map.put("data", "");
			map.put("message", "并发数过多,请稍后再试!");
			map.put("status", new Integer(500));
			JSONObject jsonObject = JSONObject.fromObject(map);
			result = jsonObject.toString();
			out.print(result);
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
	}
	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
	}
}

//D部分:清除map内容,也可以在B部分中自己实现
package com.filter;
import java.util.LinkedHashMap;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service("commonService")
public class CommonService {
	// 防止内存占用过多  每20分钟清除一次内存
	@Scheduled(cron="0 0/20 * * * ?")
	public void destoryDosFilter(){
		LinkedHashMap< String, AccessStatus> map=FrequentAccessController.getAccessMap();
		map.clear();
	}
}

4 参考资料:
https://blog.csdn.net/ElephantBoot/article/details/98732529 java控制api请求接口的次数(附代码)
https://blog.csdn.net/weixin_34223655/article/details/93656007?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control Java过滤器—Filter用法简介
https://www.cnblogs.com/nnngu/p/8626320.html Java过滤器Filter的使用详解
https://www.it610.com/article/1281441079366795264.htm java中过滤器不起作用的原因

以上是关于系统运维系列 之java控制api接口请求次数的主要内容,如果未能解决你的问题,请参考以下文章

系统运维系列 之java中实现多线程的方式

系统运维系列 之java编译运行中常见的几个错误整理

系统运维系列 之Java中synchronized详解及应用

系统运维系列 之HashMap底层实现原理和应用

系统运维系列 之Socket和ServerSocket的简单介绍(java应用)

系统运维系列 之堆栈理解(java应用)