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 0000000..e7c4eb1 Binary files /dev/null and b/spring-boot-demo-ratelimit-guava/assets/image-20190912155146012.png differ diff --git a/spring-boot-demo-ratelimit-guava/assets/image-20190912155209716.png b/spring-boot-demo-ratelimit-guava/assets/image-20190912155209716.png new file mode 100644 index 0000000..6750365 Binary files /dev/null and b/spring-boot-demo-ratelimit-guava/assets/image-20190912155209716.png differ diff --git a/spring-boot-demo-ratelimit-guava/assets/image-20190912155229745.png b/spring-boot-demo-ratelimit-guava/assets/image-20190912155229745.png new file mode 100644 index 0000000..9ddcc71 Binary files /dev/null and b/spring-boot-demo-ratelimit-guava/assets/image-20190912155229745.png differ