From 42f46b95a93898e232033130da740a2b1da1226c Mon Sep 17 00:00:00 2001
From: "Yangkai.Shen" <237497819@qq.com>
Date: Thu, 12 Sep 2019 15:55:26 +0800
Subject: [PATCH] =?UTF-8?q?:sparkles:=20spring-boot-demo-ratelimit-guava?=
=?UTF-8?q?=20=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
spring-boot-demo-ratelimit-guava/README.md | 213 ++++++++++++++++++
.../assets/image-20190912155146012.png | Bin 0 -> 44522 bytes
.../assets/image-20190912155209716.png | Bin 0 -> 48808 bytes
.../assets/image-20190912155229745.png | Bin 0 -> 36346 bytes
4 files changed, 213 insertions(+)
create mode 100644 spring-boot-demo-ratelimit-guava/assets/image-20190912155146012.png
create mode 100644 spring-boot-demo-ratelimit-guava/assets/image-20190912155209716.png
create mode 100644 spring-boot-demo-ratelimit-guava/assets/image-20190912155229745.png
diff --git a/spring-boot-demo-ratelimit-guava/README.md b/spring-boot-demo-ratelimit-guava/README.md
index a07ee9f..fb19da2 100644
--- a/spring-boot-demo-ratelimit-guava/README.md
+++ b/spring-boot-demo-ratelimit-guava/README.md
@@ -1,2 +1,215 @@
# spring-boot-demo-ratelimit-guava
+> 此 demo 主要演示了 Spring Boot 项目如何通过 AOP 结合 Guava 的 RateLimiter 实现限流,旨在保护 API 被恶意频繁访问的问题。
+
+## 1. 主要代码
+
+### 1.1. pom.xml
+
+```xml
+
+
+ * 限流注解,添加了 {@link AliasFor} 必须通过 {@link AnnotationUtils} 获取,才会生效 + * + * @author yangkai.shen + * @date Created in 2019/9/12 14:14 + * @see AnnotationUtils + *
+ */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter { + int NOT_LIMITED = 0; + + /** + * qps + */ + @AliasFor("qps") double value() default NOT_LIMITED; + + /** + * qps + */ + @AliasFor("value") double qps() default NOT_LIMITED; + + /** + * 超时时长 + */ + int timeout() default 0; + + /** + * 超时时间单位 + */ + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; +} +``` + +### 1.3. 定义一个切面 `RateLimiterAspect.java` + +```java +/** + *+ * 限流切面 + *
+ * + * @author yangkai.shen + * @date Created in 2019/9/12 14:27 + */ +@Slf4j +@Aspect +@Component +public class RateLimiterAspect { + private static final com.google.common.util.concurrent.RateLimiter RATE_LIMITER = com.google.common.util.concurrent.RateLimiter.create(Double.MAX_VALUE); + + @Pointcut("@annotation(com.xkcoding.ratelimit.guava.annotation.RateLimiter)") + public void rateLimit() { + + } + + @Around("rateLimit()") + public Object pointcut(ProceedingJoinPoint point) throws Throwable { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + // 通过 AnnotationUtils.findAnnotation 获取 RateLimiter 注解 + RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class); + if (rateLimiter != null && rateLimiter.qps() > RateLimiter.NOT_LIMITED) { + double qps = rateLimiter.qps(); + log.debug("【{}】的QPS设置为: {}", method.getName(), qps); + // 重新设置 QPS + RATE_LIMITER.setRate(qps); + // 尝试获取令牌 + if (!RATE_LIMITER.tryAcquire(rateLimiter.timeout(), rateLimiter.timeUnit())) { + throw new RuntimeException("手速太快了,慢点儿吧~"); + } + } + return point.proceed(); + } +} +``` + +### 1.4. 定义两个API接口用于测试限流 + +```java +/** + *+ * 测试 + *
+ * + * @author yangkai.shen + * @date Created in 2019/9/12 14:22 + */ +@Slf4j +@RestController +public class TestController { + + @RateLimiter(value = 1.0, timeout = 300) + @GetMapping("/test1") + public Dict test1() { + log.info("【test1】被执行了。。。。。"); + return Dict.create().set("msg", "hello,world!").set("description", "别想一直看到我,不信你快速刷新看看~"); + } + + @GetMapping("/test2") + public Dict test2() { + log.info("【test2】被执行了。。。。。"); + return Dict.create().set("msg", "hello,world!").set("description", "我一直都在,卟离卟弃"); + } +} +``` + +## 2. 测试 + +- test1 接口未被限流的时候 + + + +- test1 接口频繁刷新,触发限流的时候 + + + +- test2 接口不做限流,可以一直刷新 + + + +## 3. 参考 + +- [限流原理解读之guava中的RateLimiter](https://juejin.im/post/5bb48d7b5188255c865e31bc) + +- [使用Guava的RateLimiter做限流](https://my.oschina.net/hanchao/blog/1833612) + diff --git a/spring-boot-demo-ratelimit-guava/assets/image-20190912155146012.png b/spring-boot-demo-ratelimit-guava/assets/image-20190912155146012.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c4eb1ff4afd98cf5d0ad54188d8e40e1bf19c5 GIT binary patch literal 44522 zcmd42byS<(((p|wEl{9XixhW?TY=)m9g0Jc;u;)^yK8YMR*JhzAhTW8L3!eFQWH2DJsH(v~-)AdZ}85
zSz4y|0vnBCM~yr)TG4{GS+QJhL%hh@m*2aHa}~uaq4 `6QehqTn4IiB6gE}Aw+TOU#k=(-lqgCD*`j)
zK2Hrv$8A1;bM2<*T}_>saeX2vY~J{2FK=0itj)HsAn$DLFehgCFmyMbJ}mk0cbeL~
zpvrndvId{kV8P)GEM72aHe^Uik Su-L5{7#F;B&fuu(!(-tov
z&IIr%ZYY~Lu>=IcO~
PTp5HIUG$Gz#odY8PJ?md`GGs06x=Y+)d$3SR^Mzdau;tMnkTNsmFBU!S=cxh!
z=Hqh5K%dPt&{tyyfT`c5ts)Gv<3E+%ckzg80w_aG$3gENz+znq(bL7{OBRea3z92gCa_~)smBjrH|Ok%Ux-$a
z{n&RnX?WZo8m4yj<2}_n8)@VV`9u9v_jGig^yL0m;ap7
gv@0Jaw<8ew$wo)w%gz!r%D0a%;{G*%ULpVRV=3~A#-nQR1X&AACyLYxvK+b1vi
zj?KQ;y+vqznlq~nxlfe)MX}*$Nc;A!=4PZ?M90etTwBTh_)SKcV$iEJd_1NP(=z#T
zsv8auDq2+WNxyG&=UevB;`5wW`dm3SP2+z_CM#~82+qx8#vZa4Q%PvS>tL6_6!uJ(
zWzHh!Hi&plLR3~p0mpqC%X?;GINCOh9(AB#4TN@47CbqjP4Hq3Q(Bh}e_