package com.richinfo.sipop.web.admin.aop;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
/**
* 并发限流器, 可限流最大接口数为2000(默认,可在配置文件中用customRatelimiter.cacheSize.maxSize指定),
* 每个接口的最大并发数为100(默认,可在配置文件中用customRatelimiter.thread.maxSize指定),
* 不需要限流时,用@NotConcurrentLimit注解类或者方法。
* @see com.richinfo.sipop.common.annotation.NotConcurrentLimit
*
* @author WangZhJ 2018年5月25日
*/
@Aspect
@Component
public class CustomRatelimiter implements InitializingBean{
private Logger logger = LoggerFactory.getLogger(CustomRatelimiter.class);
/**
* 可限流接口最大数目,可配置,默认为2000(定义占用内存上限)
*/
@Value("${customRatelimiter.cacheSize.maxSize:2000}")
private int cacheSize;
private static final int defaultCacheSize = 2000;
/**
* 允许每秒最大并发数,可配置,默认为100
*/
@Value("${customRatelimiter.thread.maxSize:100}")
private int limit;
/**
* key:方法完整签名+当前秒数,value:并发数
*/
private LoadingCache<String, AtomicLong> counter;
@Override
public void afterPropertiesSet() throws Exception {
logger.info("----->限流器初始化开始");
logger.info("----->配置文件中用customRatelimiter.cacheSize.maxSize指定的流器缓存大小(可限流接口数为{}",cacheSize);
try {
//配置异常时,设为2000
cacheSize = Integer.parseInt(String.valueOf(cacheSize));
} catch (Exception e) {
cacheSize = defaultCacheSize;
logger.error("----->解析配置文件中用customRatelimiter.cacheSize.maxSize指定的流器缓存大小(可限流接口数出错",e);
}
//小于0时设为2000
if(cacheSize <= 0) {
cacheSize = defaultCacheSize;
}
logger.info("----->限流器缓存大小(可限流接口数)初始化为{}", cacheSize);
counter = CacheBuilder.newBuilder().maximumSize(cacheSize)
.expireAfterWrite(2, TimeUnit.SECONDS).build(new CacheLoader<String, AtomicLong>() {
@Override
public AtomicLong load(String key) throws Exception {
return new AtomicLong(0);
}
});
logger.info("----->限流器缓存大小(可限流接口数)初始化开始结束");
}
/**
* 切入点为:sipop-web-admin工程下controller层的方法:方法上有@RequestMapping注解
* 并且不被@NotConcurrentLimit注解的类和方法
*/
@Pointcut("execution(public * com.richinfo.sipop.web.admin.controller..*.*(..)) "
+ " && @annotation(org.springframework.web.bind.annotation.RequestMapping) "
+ " && !@annotation(com.richinfo.sipop.common.annotation.NotConcurrentLimit) "
+ " && !@target(com.richinfo.sipop.common.annotation.NotConcurrentLimit)")
public void limitPointer() {
}
// @Before("limitPointer()")
// public void doBefore(JoinPoint joinPoint){
// System.out.println(this.hashCode()+" "+this);
// }
@Around("limitPointer()")
public Object arround(ProceedingJoinPoint pjp) {
if (logger.isDebugEnabled()) {
logger.debug("----->限流开始:");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// URL
logger.debug("----->url={}", request.getRequestURI());
// method
logger.debug("----->method={}", request.getMethod());
// ip
logger.debug("----->ip={}", request.getRemoteAddr());
// 类方法
logger.debug("----->class={} and method name = {}", pjp.getSignature().getDeclaringTypeName(),
pjp.getSignature().getName());
// 参数
logger.debug("----->params={}", pjp.getArgs());
// 方法完整签名
logger.debug("----->method toLongString={}", pjp.getSignature().toLongString());
}
// 得到方法签名
String method = pjp.getSignature().toLongString();
// 得到当前秒
long currentSeconds = System.currentTimeMillis() / 1000;
String key = method + "." + currentSeconds;
try {
if (logger.isDebugEnabled()) {
logger.debug("----->"+key + "时的并发数为:" + counter.get(key));
}
if (counter.get(key).incrementAndGet() > limit) {
logger.error("----->已超过最大并发数");
return null;
}
} catch (ExecutionException e) {
logger.error("----->限流出错:", e);
}
try {
Object o = pjp.proceed();
return o;
} catch (Throwable e) {
logger.error("----->方法执行出错:method toLongString={}", pjp.getSignature().toLongString());
return null;
}
}
}
package com.richinfo.sipop.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 不需要进行并发控制时加此注解;
* 在类上加注解,对该类中所有方法都不限流;
* 在方法上加注解,对该方法不限流;
* 该注解不支持继承。
*
* @author WangZhJ 2018年5月25日
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotConcurrentLimit {
}