高并发学习之使用RateLimiter实现令牌桶限流
RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。
通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。
guava的maven依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
令牌桶的原理,有一个独立线程一直以一个固定的速率往桶中存放令牌,客户端去桶中获取令牌,获取到令牌,就可以访问,获取不到,说明请求过多,需要服务降级。
示例代码:
(1)请求限流注解
/**
* @创建人: hadoop
* @创建时间: 2020/2/12
* @描述:
*/
@Target(value = {ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface LimitingAnnotation {
/**
* 获取令牌超时时间
*/
long timeOut() default 0L;
/**
* 限流速率,每秒最多产生令牌数
*/
long produceRate() default 1000L;
}(2)请求限流切面
/**
* @创建人: hadoop
* @创建时间: 2020/2/12
* @描述: 服务限流降级切面,防止每秒请求数过多
*/
@Component
@Aspect
@Slf4j
public class LimitingAop {
@Value("${request.limit}")
private Integer limitValue ;
@Autowired
private HttpServletResponse response;
// 令牌桶
// limitValue : 表示每秒中生成limitValue个令牌存放在桶中
@SuppressWarnings("UnstableApiUsage")
private RateLimiter rateLimiter = RateLimiter.create(limitValue);
/**
* 限流切点
*/
@Pointcut("@annotation(com.service.bussiness.aop.LimitingAnnotation)")
public void limitingPointCut() {
}
@SuppressWarnings({"rawtypes", "UnstableApiUsage"})
@Around("limitingPointCut()")
public Object controllerAround( ProceedingJoinPoint point ) {
String description = CommonConst.EMPTYSTRING;
Method method = null;
String methodName = point.getSignature().getName();
Class[] paramTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
try {
method = point.getTarget().getClass().getMethod(methodName, paramTypes);
if ( !method.isAnnotationPresent(LimitingAnnotation.class) ) {
return null;
}
} catch ( NoSuchMethodException e ) {
log.error("限流切面出现异常,异常原因是: " + e.getMessage());
throw new CustomException(
Integer.parseInt(CustomExceptionType.SYSTEM_ERROR.getCode()) ,
e.getMessage(),
"限流切面出现异常",
"请求的目标方法不存在"
);
}
LimitingAnnotation limitingAnnotation =
method.getAnnotation(LimitingAnnotation.class);
if ( null == limitingAnnotation ){
return null;
}
long timeOut = limitingAnnotation.timeOut();
long produceRate = limitingAnnotation.produceRate();
// 设置限流速率,每秒产生多少令牌
rateLimiter.setRate(produceRate);
// 每次发送请求,在设定ms内没有获取到令牌,则对服务进行降级处理
boolean acquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
if ( !acquire ){
getErrorMsg();
return null;
}
try {
return point.proceed();
} catch (Throwable throwable) {
log.error(methodName+"请求出现异常,异常原因是: " + throwable.getMessage());
throwable.printStackTrace();
}
return null;
}
/**
* 向客户端输出服务降级信息
*
*/
public void getErrorMsg(){
response.setHeader("Content-Type","application/json;charset=UTF-8");
PrintWriter printWriter = null;
try {
printWriter = response.getWriter();
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("msg","前方任务过多,请稍后再试");
printWriter.write(JSON.toJSONString(resultMap));
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}(3) 请求服务
@RequestMapping(value = "/search", method = RequestMethod.POST, consumes = "application/json")
@ResponseBody
@LimitingAnnotation( timeOut = 500, produceRate = 1000 )
public ResponseEntity<String> search( @RequestBody String param ) {return this.searchService.searchBillInfo( xEncryption , params );
}参考:
相关推荐
campwin 2020-08-16
Happyunlimited 2019-11-16
yingrenzhe 2019-11-08
闫新宇 2017-05-10
dushine00 2019-07-01
wuxiaosi0 2019-07-01
wuxiaosi0 2019-06-29
Broadview 2019-06-28
duyifei0 2019-06-27
WindChaser 2019-06-27
bamboocqh 2019-04-27
sdwylry 2018-12-12
张小染 2019-04-17
yhguo00 2018-03-21
StrongHYQ 2019-03-05
纬纬 2018-05-17