您好,登錄后才能下訂單哦!
這篇文章主要講解了“SpringBoot怎么使用RateLimiter通過(guò)AOP方式進(jìn)行限流”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“SpringBoot怎么使用RateLimiter通過(guò)AOP方式進(jìn)行限流”吧!
<!-- 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 { 每秒只發(fā)出5個(gè)令牌,此處是單進(jìn)程服務(wù)的限流,內(nèi)部采用令牌捅算法實(shí)現(xiàn) private static RateLimiter rateLimiter = RateLimiter.create(5.0); //Service層切點(diǎn) 限流 @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; } }
@Override @ServiceLimit @Transactional public Result startSeckil(long seckillId,long userId) { //todo 操作 }
令牌桶和漏桶
漏桶算法 的實(shí)現(xiàn)往往依賴于隊(duì)列,請(qǐng)求到達(dá)如果隊(duì)列未滿則直接放入隊(duì)列,然后有一個(gè)處理器按照固定頻率從隊(duì)列頭取出請(qǐng)求進(jìn)行處理。如果請(qǐng)求量大,則會(huì)導(dǎo)致隊(duì)列滿,那么新來(lái)的請(qǐng)求就會(huì)被拋棄。
令牌桶算法 則是一個(gè)存放固定容量令牌的桶,按照固定速率往桶里添加令牌。桶中存放的令牌數(shù)有最大上限,超出之后就被丟棄或者拒絕。當(dāng)流量或者網(wǎng)絡(luò)請(qǐng)求到達(dá)時(shí),每個(gè)請(qǐng)求都要獲取一個(gè)令牌,如果能夠獲取到,則直接處理,并且令牌桶刪除一個(gè)令牌。如果獲取不到,該請(qǐng)求就要被限流,要么直接丟棄,要么在緩沖區(qū)等待。
令牌桶和漏桶對(duì)比
令牌桶是按照固定速率往桶中添加令牌,請(qǐng)求是否被處理需要看桶中令牌是否足夠,當(dāng)令牌數(shù)減為零時(shí)則拒絕新的請(qǐng)求;漏桶則是按照常量固定速率流出請(qǐng)求,流入請(qǐng)求速率任意,當(dāng)流入的請(qǐng)求數(shù)累積到漏桶容量時(shí),則新流入的請(qǐng)求被拒絕;
令牌桶限制的是平均流入速率,允許突發(fā)請(qǐng)求,只要有令牌就可以處理,支持一次拿3個(gè)令牌,4個(gè)令牌;漏桶限制的是常量流出速率,即流出速率是一個(gè)固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2,從而平滑突發(fā)流入速率;
令牌桶允許一定程度的突發(fā),而漏桶主要目的是平滑流出速率;
1.依賴
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.1-jre</version> <optional>true</optional> </dependency>
2.示例代碼
@Slf4j @Configuration public class RequestInterceptor implements HandlerInterceptor { // 根據(jù)字符串分不同的令牌桶, 每天自動(dòng)清理緩存 private static LoadingCache<String, RateLimiter> cachesRateLimiter = CacheBuilder.newBuilder() .maximumSize(1000) //設(shè)置緩存?zhèn)€數(shù) /** * expireAfterWrite是在指定項(xiàng)在一定時(shí)間內(nèi)沒(méi)有創(chuàng)建/覆蓋時(shí),會(huì)移除該key,下次取的時(shí)候從loading中取 * expireAfterAccess是指定項(xiàng)在一定時(shí)間內(nèi)沒(méi)有讀寫,會(huì)移除該key,下次取的時(shí)候從loading中取 * refreshAfterWrite是在指定時(shí)間內(nèi)沒(méi)有被創(chuàng)建/覆蓋,則指定時(shí)間過(guò)后,再次訪問(wèn)時(shí),會(huì)去刷新該緩存,在新值沒(méi)有到來(lái)之前,始終返回舊值 * 跟expire的區(qū)別是,指定時(shí)間過(guò)后,expire是remove該key,下次訪問(wèn)是同步去獲取返回新值; * 而refresh則是指定時(shí)間后,不會(huì)remove該key,下次訪問(wèn)會(huì)觸發(fā)刷新,新值沒(méi)有回來(lái)時(shí)返回舊值 */ .expireAfterAccess(1, TimeUnit.HOURS) .build(new CacheLoader<String, RateLimiter>() { @Override public RateLimiter load(String key) throws Exception { // 新的字符串初始化 (限流每秒2個(gè)令牌響應(yīng)) return RateLimiter.create(2); } }); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("request請(qǐng)求地址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) { // 解決攔截器的異常,全局異常處理器捕獲不到的問(wèn)題 request.setAttribute("exception", e); request.getRequestDispatcher("/error").forward(request, response); } return true; } }
3.測(cè)試
@RestController @RequestMapping(value = "user") public class UserController { @GetMapping public Result test2(){ System.out.println("1111"); return new Result(true,200,""); } }
http://localhost:8080/user/
如果沒(méi)有result類,自己可以隨便返回個(gè)字符串
4.測(cè)試結(jié)果
創(chuàng)建
RateLimiter提供了兩個(gè)工廠方法:
一個(gè)是平滑突發(fā)限流
RateLimiter r = RateLimiter.create(5); //項(xiàng)目啟動(dòng),直接允許5個(gè)令牌
一個(gè)是平滑預(yù)熱限流
RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS); //項(xiàng)目啟動(dòng)后3秒后才會(huì)到達(dá)設(shè)置的2個(gè)令牌
缺點(diǎn)
RateLimiter只能用于單機(jī)的限流,如果想要集群限流,則需要引入redis或者阿里開(kāi)源的sentinel中間件。
TimeUnit.SECONDS);` //項(xiàng)目啟動(dòng)后3秒后才會(huì)到達(dá)設(shè)置的2個(gè)令牌
感謝各位的閱讀,以上就是“SpringBoot怎么使用RateLimiter通過(guò)AOP方式進(jìn)行限流”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)SpringBoot怎么使用RateLimiter通過(guò)AOP方式進(jìn)行限流這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。