AOP+Semaphore实现单节点的接口(方法)限流
AOP+Semaphore 对项目限流
Semaphore限流是从线程的个数限流。
RateLimiter是从速率限流,目前的算法有漏桶算法和令牌算法。
0、依赖
1、自定义注解
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface RateLimit { /** * 资源的名称 * * @return */ String name() default ""; /** * 资源的key * * @return */ String key() default ""; /** * Key的prefix * * @return */ String prefix() default ""; /** * 给定的时间段 单位秒 * * @return */ int period(); /** * 最多的访问限制次数 * * @return */ int limitNum(); /** * 类型 * * @return */ LimitType limitType() default LimitType.CUSTOMER;
public enum LimitType { /** * 自定义key */ CUSTOMER, /** * 根据请求者IP */ IP; }
2、限流切面
@Component @Scope @Aspect public class ApiRateLimitAspect { private Logger logger = LoggerFactory.getLogger(ApiRateLimitAspect.class); //用来存放不同类名$方法名的Semaphore(key为接口名称,value为Semaphore) private ConcurrentHashMap<String, Semaphore> map = new ConcurrentHashMap<>(); private Semaphore semaphore; @Pointcut("@annotation(ratelimit.annotation.RateLimit)") public void rateLimit() { } @Around("rateLimit()") public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { Object obj = null; //获取拦截的方法名 MethodSignature msig = (MethodSignature)joinPoint.getSignature(); Method currentMethod = msig.getMethod(); //获取注解信息 RateLimit annotation = currentMethod.getAnnotation(RateLimit.class); int limitNum = annotation.limitNum(); //每秒限流的数量 String functionName = currentMethod.getDeclaringClass().getName() + "#" + currentMethod.getName();//msig.getName(); // 注解所在方法名区分不同的限流策略 if(map.containsKey(functionName)){ semaphore = map.get(functionName); }else { map.put(functionName, new Semaphore(limitNum, false)); semaphore = map.get(functionName); } //方案1:超过阀值时,新请求被阻塞 try { //无参方法tryAcquire()的作用是尝试的获得1个许可,如果获取不到则返回false,该方法通常与if语句结合使用,其具有无阻塞的特点。无阻塞的特点可以使线程不至于在同步处一直持续等待的状态,如果if语句判断不成立则线程会继续走else语句,程序会继续向下运行。 if (semaphore.tryAcquire()) { //执行方法 obj = joinPoint.proceed(); } else { //拒绝了请求(服务降级) return "系统繁忙,稍后再试"; } } catch (Throwable throwable) { } finally { //释放一个许可证,默认是1 semaphore.release(); } //方案2:超过阀值时,新请求被阻塞,2选1啊 try { //获取1个许可证,如果没有可用许可证,一直阻塞这里 semaphore.acquire(1); //执行方法 obj = joinPoint.proceed(); } catch (Throwable throwable) { } finally { //释放一个许可证,默认也是1,会将指定的线程数释放,然后唤醒等待的线程 semaphore.release(); } return obj; } }
3、使用注解
@Slf4j @Controller public class HelloController { @RateLimit(5) @RequestMapping("/hello") @ResponseBody public String hello(){ //假设业务逻辑执行了x秒 sleep(); return "hello"; }
4、测试:jmeter调用
参考:https://blog.csdn.net/weixin_42412601/article/details/105166850