SpringBoot中间件封装限流器的方案详解
背景
通常能知道一个系统服务在正产增速下流量大小,扩容与压测也是基于此。若有突发或者恶意攻击访问,都要将流量拦截在外。这部分功能不属于业务侧,它是通用非业务的共性需求,所以我们将共性抽取为限流中间件。
方案设计
图解:
Ratelimiter
- 谷歌Guava库中的一个限流工具类,用于限制访问限制某一资源,令牌桶思想的一个实现,可实现流量控制。
用到其两个方法:
- create():创建一个RateLimiter实例,并设置该实例的限流速率(即允许每秒执行的任务数),例如:
RateLimiter rateLimiter = RateLimiter.create(10);
上述代码将创建一个每秒允许10个任务的RateLimiter实例。
tryAcquire():尝试获取一个许可,若获取成功则返回true,否则返回false。 例如:
if (rateLimiter.tryAcquire()) { // do something }
上述代码将尝试获取一个许可,如果该实例当前拥有足够的许可,则执行do something操作
使用自定义注解和AOP,拦截需要被限流保护的方法。
拦截后,用Ratelimiter做限流处理
- 使用自定义注解和AOP,拦截需要被限流保护的方法。
- 拦截后,用Ratelimiter做限流处理
代码示例
1.自定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DoRateLimiter { // 每秒访问次数限制 public double rate() default 0d; // 超频后的触发熔断默认响应结果 public String returnJson() default ""; }
2.常量类
public class Constant { /** * 全局Hashmap,用于存放【方法】对应的限流类 * key: 类名 + 方法名 * value: 对应限流类 */ public static Map<String, RateLimiter> stringRateLimiterMap = Collections.synchronizedMap(new HashMap<String, RateLimiter>()); }
3.切面逻辑处理
PS:切面中的实现细节/注解的使用已经在统一白名单中间件 文章中详细梳理过了,需要请移步
@Component @Aspect public class RateLimiterHandle { // 切点 @Pointcut("@annotation(com.example.ratelimiteer.annotation.DoRateLimiter)") public void aopPointcut() { } /** * @param jp 切点 * @param doRateLimiter 注解实例 * @return 方法响应结果 * @throws Throwable */ @Around(value = "aopPointcut()&& @annotation(doRateLimiter)") public Object handle(ProceedingJoinPoint jp, DoRateLimiter doRateLimiter) throws Throwable { // 判断是否开启限流 if (doRateLimiter.rate() == 0d) { return jp.proceed(); } // 构建限流哈希Map的Key: 类名 + "." + "方法名" String className = jp.getTarget().getClass().getName(); MethodSignature signature = (MethodSignature) jp.getSignature(); Method method = jp.getTarget().getClass().getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes()); String methodName = method.getName(); String key = className + "." + methodName; // 第一次走到该接口 if (!Constant.stringRateLimiterMap.containsKey(key)) { Map<String, RateLimiter> rateLimiterMap = Constant.stringRateLimiterMap; rateLimiterMap.put(key, RateLimiter.create(doRateLimiter.rate())); } // 判断是否限流 RateLimiter rateLimiter = Constant.stringRateLimiterMap.get(key); if (rateLimiter.tryAcquire()) { // 未超过访问限制,则继续执行对应业务逻辑 return jp.proceed(); } else { // 超过访问限制,则返回默认熔断响应结果(即注解中的returnJson) return JSON.parseObject(doRateLimiter.returnJson()); } } }
测试
controller如下,仅允许一个并发
压测两个并发
result:尝试拿一个许可(RateLimiter.tryAcquire())未拿到(即超频),返回注解中的returnJson()
到此这篇关于SpringBoot中间件封装限流器的文章就介绍到这了,更多相关SpringBoot限流器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论