시간이 지남에 따라 시스템은 일정한 1/QPS 시간 간격(QPS=100인 경우 간격은 10ms)으로 버킷에 토큰을 추가합니다(누수와 반대되는 상황을 상상해 보세요). ), 지속적으로 물을 추가하는 수도꼭지가 있습니다. 물통이 가득 차면 더 이상 물이 추가되지 않습니다. 가져갈 토큰이 없으면 각각 토큰을 가져갑니다.
주문 토큰 버킷의 또 다른 이점은 속도를 쉽게 변경할 수 있다는 것입니다. 일단 비율을 높여야 하면 버킷에 들어가는 토큰의 비율이 일반적으로 증가합니다. 토큰 수는 정기적으로(예: 100밀리초) 추가됩니다. 일부 변형 알고리즘은 실시간으로 추가되어야 하는 토큰 수를 계산합니다.
토큰 버킷은 일반적으로 사용되는 흐름 제어 기술입니다. 토큰 버킷 자체에는 폐기 및 우선순위 정책이 없습니다.
1. 토큰은 특정 비율로 버킷에 넣어집니다.
2. 각 토큰을 통해 소스는 특정 수의 비트를 보낼 수 있습니다.
3. 패킷을 전송하기 위해 트래픽 조절기는 버킷에서 패킷 크기와 동일한 수의 토큰을 제거합니다.
4. 패킷을 보내기에 토큰이 충분하지 않은 경우 패킷은 토큰이 충분할 때까지 기다리거나(셰이퍼의 경우) 패킷이 삭제될 수 있으며 더 낮은 DSCP로 표시될 수 있습니다(정책 프로그램의 경우).
5. 버킷에는 특정 용량이 있습니다. 버킷이 가득 차면 새로 추가된 토큰이 폐기됩니다. 따라서 소스가 언제든지 네트워크에 전송할 수 있는 최대 버스트 데이터 양은 버킷 크기에 비례합니다. 토큰 버킷은 버스트를 허용하지만 한도를 초과할 수는 없습니다.
수정자 및 유형 | 메서드 및 설명 |
---|---|
double | acquire() er A 권한, 요청을 얻을 때까지 메서드가 차단됩니다 |
double | acquire(int alloweds) RateLimiter에서 지정된 수의 허가를 얻습니다. 이 메소드는 요청이 얻어질 때까지 차단됩니다. |
static RateLimiter | create(double allowedsPerSecond) 지정된 안정적인 처리량에 따라 생성합니다. rate RateLimiter, 여기서 처리량 비율은 지정된 안정적인 처리량에 따라 초당 허용 수(보통 QPS, 초당 쿼리 수)를 나타냅니다. |
static RateLimiter | create(double allowedsPerSecond, long WarmupPeriod, TimeUnit 단위) 여기에서 처리량 속도는 초당 라이센스 수(일반적으로 QPS, 초당 요청 수)를 나타냅니다. 초당 증가는 워밍업 기간이 끝날 때 최대 속도에 도달할 때까지 발생합니다. (포화시킬 만큼 충분한 요청이 있는 한) |
double | getRate()는 RateLimiter 구성에서 안정적인 속도를 반환하며, 속도 단위는 초당 허용 횟수입니다 |
void | setRate( double allowedsPerSecond) RateLimite의 안정적인 속도를 업데이트합니다. 매개변수 allowedPerSecond는 RateLimiter를 구성하는 팩토리 메서드에서 제공됩니다. |
String | toString() 객체의 문자 표현을 반환합니다. |
boolean | tryAcquire() 지연 없이 즉시 권한을 얻을 수 있는 경우 RateLimiter에서 권한을 가져옵니다. |
boolean | tryAcquire (int alloweds) RateLimiter에서 권한 개수를 가져옵니다. 지연 없이 즉시 권한을 얻을 수 있는 개수 |
boolean | tryAcquire(int alloweds, long timeout, TimeUnit 단위)를 RateLimiter에서 지정 개수의 라이선스를 가져옵니다. 제한 시간을 초과하지 않는 기간 내에 라이센스 수를 얻을 수 있거나 제한 시간이 만료되기 전에 라이센스 수를 얻을 수 없는 경우 즉시 false를 반환합니다(기다릴 필요 없음) |
boolean | tryAcquire( long timeout, TimeUnit 단위) RateLimiter에서 권한 얻기 제한 시간을 초과하지 않는 시간 내에 권한을 얻을 수 있거나 제한 시간이 만료되기 전에 권한을 얻을 수 없는 경우 즉시 false를 반환합니다(기다릴 필요 없음) |
pom.xml
<!--guava RateLimiter限流--> <!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.2-jre</version> </dependency>
사용자 정의 인터페이스 Limit
package com.zjy.knife4j.inte; import java.lang.annotation.*; /** * 限流注解 */ @Inherited @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Limit { // 默认每秒放入桶中的token double limitNum() default 20; String name() default ""; }
aop 양상
package com.zjy.knife4j.aspect; import com.google.common.util.concurrent.RateLimiter; import com.zjy.knife4j.inte.Limit; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; @Aspect @Component public class RateLimitAspect { /**日志对象*/ private static final Logger logger = LoggerFactory.getLogger(RateLimitAspect.class); private ConcurrentHashMap<String, RateLimiter> RATE_LIMITER = new ConcurrentHashMap<>(); private RateLimiter rateLimiter; @Pointcut("@annotation(com.zjy.knife4j.inte.Limit)") public void serviceLimit() { } @Around("serviceLimit()") public Object around(ProceedingJoinPoint point) throws Throwable { Object obj = null; //获取拦截的方法名 Signature sig = point.getSignature(); //获取拦截的方法名 MethodSignature msig = (MethodSignature) sig; //返回被织入增加处理目标对象 Object target = point.getTarget(); //为了获取注解信息 Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); //获取注解信息 Limit annotation = currentMethod.getAnnotation(Limit.class); double limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token String functionName = msig.getName(); // 注解所在方法名区分不同的限流策略 if(RATE_LIMITER.containsKey(functionName)){ rateLimiter=RATE_LIMITER.get(functionName); }else { RATE_LIMITER.put(functionName, RateLimiter.create(limitNum)); rateLimiter=RATE_LIMITER.get(functionName); } if(rateLimiter.tryAcquire()) { logger.info("执行成功!!!...做一些业务处理"); return point.proceed(); } else { logger.info("请求繁忙...做一些业务处理"); return null; } } }
RateLimiterController
package com.zjy.knife4j.controller; import com.zjy.knife4j.inte.Limit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/ratelimiter") @RestController public class RateLimiterController { /** * 开启限流 * @return */ @GetMapping("/open") @Limit(limitNum = 1, name = "test1") public String openRateLimiter1() { System.out.println("【限流执行了....编写业务....】"); return "限流执行了"; } /** * 开启限流 * @return */ @GetMapping("/open2") @Limit(limitNum = 1, name = "test2") public String openRateLimiter2() { System.out.println("【限流执行了222】"); return "限流执行了222"; } /** * 未开启限流 * @return */ @GetMapping("/close") public String closeRateLimiter() { System.out.println("【不限流执行了】"); return "不限流执行了"; } }
서비스 시작 및 현재 제한 주석을 추가하는 인터페이스에 액세스
주석이 없는 인터페이스에 다시 액세스
콘솔에 결과가 인쇄됩니다.
위 내용은 Springboot를 사용하여 RateLimiter 전류 제한을 통합하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!