<!-- guava 限流 --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>25.1-jre</version> </dependency>
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ServiceLimit { String description() default ""; }
@Component @Scope @Aspect public class LimitAspect { 每秒只发出5个令牌,此处是单进程服务的限流,内部采用令牌捅算法实现 private static RateLimiter rateLimiter = RateLimiter.create(5.0); //Service层切点 限流 @Pointcut("@annotation(com.itstyle.seckill.common.aop.ServiceLimit)") public void ServiceAspect() { } @Around("ServiceAspect()") public Object around(ProceedingJoinPoint joinPoint) { Boolean flag = rateLimiter.tryAcquire(); Object obj = null; try { if(flag){ obj = joinPoint.proceed(); } } catch (Throwable e) { e.printStackTrace(); } return obj; } }
리키 버킷 알고리즘의 구현은 종종 큐에 의존합니다. 요청이 도착하면 큐가 가득 차지 않으면 바로 큐에 넣은 다음 프로세서가 꺼냅니다. 처리를 위해 고정된 빈도로 대기열 헤드의 요청입니다. 요청량이 크면 대기열이 가득 차고 새 요청이 삭제됩니다.
토큰 버킷 알고리즘은 고정된 용량으로 토큰을 저장하는 버킷으로, 토큰은 고정된 비율로 버킷에 추가됩니다. 버킷에 저장되는 토큰 수에는 최대 제한이 있습니다. 초과되면 폐기되거나 거부됩니다. 트래픽이나 네트워크 요청이 도착하면 각 요청은 토큰을 얻어야 합니다. 토큰을 얻을 수 있는 경우 직접 처리되며 토큰 버킷에서 토큰이 삭제됩니다. 요청을 얻을 수 없는 경우 요청은 흐름이 제한되고 직접 삭제되거나 버퍼에서 대기됩니다.
토큰 버킷은 버킷에 토큰이 충분한지 여부에 따라 고정된 속도로 버킷에 토큰을 추가합니다. 0 시간에 새 요청은 일정한 고정 비율로 유출 요청을 거부하며 수신 요청 수가 누출 버킷 용량에 누적되면 새 수신 요청이 거부됩니다.
@Override @ServiceLimit @Transactional public Result startSeckil(long seckillId,long userId) { //todo 操作 }
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.1-jre</version> <optional>true</optional> </dependency>http:// localhost:8080/user/
결과 클래스가 없으면 원하는 대로 문자열을 반환할 수 있습니다.
4. 테스트 결과
기타
만들기
RateLimiter는 두 가지 팩토리 메서드를 제공합니다.
하나는 부드러운 버스트 전류 제한@Slf4j @Configuration public class RequestInterceptor implements HandlerInterceptor { // 根据字符串分不同的令牌桶, 每天自动清理缓存 private static LoadingCache<String, RateLimiter> cachesRateLimiter = CacheBuilder.newBuilder() .maximumSize(1000) //设置缓存个数 /** * expireAfterWrite是在指定项在一定时间内没有创建/覆盖时,会移除该key,下次取的时候从loading中取 * expireAfterAccess是指定项在一定时间内没有读写,会移除该key,下次取的时候从loading中取 * refreshAfterWrite是在指定时间内没有被创建/覆盖,则指定时间过后,再次访问时,会去刷新该缓存,在新值没有到来之前,始终返回旧值 * 跟expire的区别是,指定时间过后,expire是remove该key,下次访问是同步去获取返回新值; * 而refresh则是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值 */ .expireAfterAccess(1, TimeUnit.HOURS) .build(new CacheLoader<String, RateLimiter>() { @Override public RateLimiter load(String key) throws Exception { // 新的字符串初始化 (限流每秒2个令牌响应) return RateLimiter.create(2); } }); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("request请求地址path[{}] uri[{}]", request.getServletPath(), request.getRequestURI()); try { String str = "hello"; // 令牌桶 RateLimiter rateLimiter = cachesRateLimiter.get(str); if (!rateLimiter.tryAcquire()) { System.out.println("too many requests."); return false; } } catch (Exception e) { // 解决拦截器的异常,全局异常处理器捕获不到的问题 request.setAttribute("exception", e); request.getRequestDispatcher("/error").forward(request, response); } return true; } }
@RestController @RequestMapping(value = "user") public class UserController { @GetMapping public Result test2(){ System.out.println("1111"); return new Result(true,200,""); } }단점 RateLimiter는 단일 시스템의 전류를 제한하는 데에만 사용할 수 있습니다. 클러스터의 전류를 제한하려는 경우, redis나 Alibaba의 오픈 소스 Sentinel 미들웨어를 도입해야 합니다.
아아아아
위 내용은 SpringBoot는 AOP를 통해 전류를 제한하기 위해 어떻게 RateLimiter를 사용합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!