前言
在日常開發工作中,我們常有接口會暴露出來,雖然我們增加了各種檢驗和攔截可以攔截大多數惡意通路,但是你不能保證對接方的猿子不會造出一個死循環來通路你的接口,尤其是我們的程式作為一個平台使用的時候,别人的一個誤操作可能會造成伺服器當機,到時候成千上萬的客戶都會受到影響,是以在這種對接過程中一定要對對方的接口通路次數進行限制!這種方式可以了解為微服務中的服務降級!
安排栗子
建立一個注釋類:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitTime {
// 通路次數,預設為10次
int time() default 10;
// 過期時間,時間戳間隔
long timeout() default 1;
}
定義一個存放調用資訊的DTO:
@Data
public class LimitDTO {
//最近一次重新整理時間戳
private Long refreshTime;
//剩餘通路次數
private Integer time;
}
建立一個切面類:
注意在存儲通路狀态對象的時候一定要使用ConcurrentHashMap,此為線程安全的map,支援并發通路!
@Component
@Order
@Aspect
public class LimitTimeAspect {
private ConcurrentHashMap<String, LimitDTO> limitMap = new ConcurrentHashMap<>();
@Pointcut("@annotation(limitTime)")
public void limit(LimitTime limitTime) {
}
@Around("limit(limitTime)")
public Object aroundLog(ProceedingJoinPoint joinpoint, LimitTime limitTime) throws Throwable {
//擷取傳入的最大通路次數
int time= limitKey.time();
//擷取計算時間
long timeout = limitKey.timeout();
//擷取通路方法
Object target = joinpoint.getTarget().getClass().getName();
String key= target.toString();
//如果第一次通路該方法
if (limitMap.get(key) == null) {
//建立一次對象存放通路資訊
LimitDTO limitDTO=new LimitDTO();
limitDTO.setTime(time- 1);
limitDTO.setRefreshTime(new Date().getTime());
limitMap.put(key, limitDTO);
}else {
//如果不是第一次通路,擷取上次通路的資訊
LimitDTO limitDTO=limitMap.get(key);
//如果和上次重新整理時間比已經過期
if (new Date().getTime() - limitDTO.getRefreshTime() > timeout) {
//将對象中的重新整理時間和通路次數重新整理
limitDTO.setRefreshTime(new Date().getTime());
limitDTO.setTime(time);
limitMap.put(key, limitDTO);
}
//擷取目前通路對象中的剩餘通路次數
int t = (int) limitMap.get(key).getTime;
//如果通路次數大于0
if (t > 0) {
//允許通路,并将通路次數-1
limitDTO.setTime(--t);
} else {
//如果已經沒有通路次數,傳回錯誤資訊
ResultBO<Object> resultBO = new ResultBO<>();
resultBO.setCode(1);
resultBO.setMsg("已達最大通路次數");
return resultBO;
}
}
//列印資訊
System.err.println("剩餘次數:" + limitMap.get(key).getTime + " 方法名稱:" + key);
return joinpoint.proceed();
}
使用該注釋:
@RequestMapping(value = "/sendCmd", method = RequestMethod.POST)
@ResponseBody
@LimitKey(time= 10, timeout = 10000)//10秒内可以通路10次
public int sendCmd(@RequestBody List<CmdDO> cmds) {
//do something
return new ResultBO<>();
}
測試:

撒花!完成!