我的Springboot应用中使用拦截器做限流,在preHandle中申请许可,在执行完之后归还许可,一开始是在postHandle中归还许可,但后来发现,如果接口不自己处理异常,而是抛出异常,则可用的许可越来越少,即,接口抛出异常,则postHandle中的归还许可代码不会执行。
解决办法:
把归还许可的代码放到afterCompletion方法中即可,原因见下面的源码(org.springframework.web.servlet.HandlerExecutionChain),即使preHandle执行不成功,则直接执行触发afterCompletion方法的triggerAfterCompletion方法:
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
附上拦截器源码:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("Init permits numbers : " + RateLimiterMap.permit.availablePermits());
boolean acq = RateLimiterMap.permit.tryAcquire();//获取许可
logger.info("Permits available numbers : " + RateLimiterMap.permit.availablePermits());
if(!acq){
//无可用许可,直接抛出异常
throw new GraphOperateException("The whole request amount has exceed the ability that Model Service can afford");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("postHandle was executed");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
RateLimiterMap.permit.release();//归还许可
logger.info("AfterCompletion was executed: Permits available numbers : " + RateLimiterMap.permit.availablePermits());
}