RateLimiter API限流
注:并没有做深入研究,只是简单记录 RateLimiter 的使用方法。
RateLimiter 与 Semphore :
- RateLimiter 是 google 的 guava 工具包中提供的限流工具类,而 Semphore 是 jdk 中提供的信号量类。两者都可做限流。
- RateLimiter 是基于速率限流,常见算法有漏桶算法和令牌桶算法;Sempgore 是基于并发量限流(也就是数量),它是通过线程个数去限流的。
- RateLimiter 的原理是系统以一个恒定的速度往桶里放令牌,请求到达时先从桶里获取令牌,然后处理请求,若没有获取到令牌则不处理。当桶里的令牌被获取完,之后到达的请求只能等待,直到下一个令牌产生。当桶里的令牌到达某一个数量时,新产生的令牌会被丢弃。
- Semphore 的原理时通过限制线程个数来达到限流的目的。处理完请求后需要被释放,才能处理下一个。例如 Semphore 的并发量是10个,当第十一个请求到达后,只能等前十个中的某一个请求处理完并释放令牌后才能被处理;RateLimiter 的并发量是10个,当第十一个请求到达后,如果桶里有令牌那就可被处理,如果没有,则等待下一个令牌产生就可被处理。
以 RateLimiter 为例 :
自定义注解和 aop 搭配的方式,如果是分布式则可使用 redis 配合。
1、自定义注解(此处只作为一个标识)
@Inherited // 允许被继承
@Target(ElementType.METHOD) // 使用范围: 方法
@Retention(RetentionPolicy.RUNTIME) // 有效范围: 运行时
public @interface RateLimit {
// 只作为标识
}
2、aop 切面处理
@Aspect
@Component
public class RateLimitAop {
@Autowired
private HttpServletResponse response;
private RateLimiter limiter = RateLimiter.create(3.0); // QPS
/**
* 切点
*/
@Pointcut("@annotation(org.xgllhz.base.common.annotation.RateLimit)")
public void rateLimitPointcut() {
//
}
/**
* 通知
* @param joinPoint
* @return
*/
@Around("rateLimitPointcut()")
public Object rateLimitAround(ProceedingJoinPoint joinPoint) {
Boolean flag = limiter.tryAcquire(); // 尝试获取令牌
Object object = null;
try {
if (flag) { // 如果获取到令牌
object = joinPoint.proceed(); // 处理请求
System.out.println("flag: " + flag + " object: " + object);
} else { // 如果未获取到则返回
System.out.println("flag: " + flag + " object: " + object);
ObjectMapper mapper = new ObjectMapper();
PrintWriter writer = response.getWriter();
writer.write(mapper.writeValueAsString(new APIResponse<>(ConstConfig.RE_REQUEST_BUSY_CODE,
ConstConfig.RE_REQUEST_BUSY_MSG)));
writer.flush();
writer.close();
}
} catch (Throwable e) {
e.printStackTrace();
}
return object;
}
}
3、demo
@RestController
@RequestMapping("/common")
public class CommonController {
@RateLimit
@RequestMapping("/test")
public APIResponse<Map<String, Object>> test() {
return new APIResponse<>();
}
}
4、测试结果
-
控制台
flag: true object: APIResponse(recode=200, remsg=请求成功!, body={}) flag: true object: APIResponse(recode=200, remsg=请求成功!, body={}) flag: true object: APIResponse(recode=200, remsg=请求成功!, body={}) flag: true object: APIResponse(recode=200, remsg=请求成功!, body={}) flag: false object: null flag: false object: null flag: false object: null flag: false object: null flag: false object: null flag: true object: APIResponse(recode=200, remsg=请求成功!, body={}) -
JMeter


注:关于 aop 中 response 对象的获取,提以下几点 :
-
注解方式
@Autowried private HttpServletResponse response; -
参数方式
public void test(HttpServletResponse response){} // 这种方式在普通方法中可以获取到,但在被 @Around、@Before 等切面通知注解 //作用的方法上获取不到,因为这类方法的参数不接受 HttpServletResponse 的类型参数,所以这里不适用。 -
上下文获取
// 1、在 web.xml 文件中加入以下配置 <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> // 2、在程序中加入以下代码 HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder .getRequestAttributes()).getRequest(); HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder .getRequestAttributes()).getResponse(); // 注:在低版本的 spring 中,ServletRequestAttributes 是没有 getResponse() 方法的, //只有 getRequest()方法,所以在低版本的 spring 中用这种方式获取不到 response 对象。 -
第四种方式
/** 当 spring 版本过低 时可以使用如下方式: 在被 @RateLimit 注解作用的方法中加入 HttpServletResponse 类型的形参, 然后再通知方法里通过获取被作用方法的参数来获取。 */ // 被 @RateLimit 注解作用的方法(response 参数的顺序很重要) public void test(HttpServletResponse response){} // 通知方法 @Around("rateLimitPointcut()") public Object rateLimitAround(ProceedingJoinPoint joinPoint) { Object[] objects = joinPoint.getArgs(); // 被作用方法的参数列表 HttpServletResponse response = (HttpServletResponse) objects[0]; } // 注:前置通知(@Before)的通知方式中不能添加 response 响应
@XGLLHZ - Journey.mp3
本文介绍了Guava库中的RateLimiter和JDK的Semaphore在限流策略上的区别,RateLimiter基于速率限流,Semaphore基于并发量限流。通过一个自定义注解和AOP的示例展示了RateLimiter的使用,用于限制QPS。在分布式场景下,可以结合Redis实现分布式限流。
85万+

被折叠的 条评论
为什么被折叠?



