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
分类:
juc
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
2013-05-09 ES之十:ElasticSearch监控工具 - cerebro
2013-05-09 ES之十一:elasticsearch之Routing及restClient API中如何使用route