SpringBoot
是為了簡化 Spring
應用的創建、運行、調試、部署等一系列問題而誕生的產物,自動裝配的特性讓我們可以更好的關注業務本身而不是外部的XML配置,我們只需遵循規范,引入相關的依賴就可以輕易的搭建出一個 WEB 工程
在前面的兩篇文章中,介紹了一些限流的類型和策略,本篇從 Spring Boot
、Redis
應用層面來實現分布式的限流….
分布式限流
單機版中我們了解到 AtomicInteger
、RateLimiter
、Semaphore
這幾種解決方案,但它們也僅僅是單機的解決手段,在集群環境下就透心涼了,后面又講述了 Nginx
的限流手段,可它又屬於網關層面的策略之一,並不能解決所有問題。例如供短信接口,你無法保證消費方是否會做好限流控制,所以自己在應用層實現限流還是很有必要的。
本章目標
利用 自定義注解
、Spring Aop
、Redis Cache
實現分布式限流….
具體代碼
很簡單…
導入依賴
在 pom.xml
中添加上 starter-web
、starter-aop
、starter-data-redis
的依賴即可,習慣了使用 commons-lang3
和 guava
中的一些工具包…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
<dependencies> |
屬性配置
在 application.properites
資源文件中添加 redis
相關的配置項
1 2 3
|
spring.redis.host=localhost spring.redis.port=6379 spring.redis.password=battcn
|
Limit 注解
創建一個 Limit
注解,不多說注釋都給各位寫齊全了….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
package com.battcn.limiter.annotation;
import com.battcn.limiter.LimitType;
import java.lang.annotation.*;
|
RedisTemplate
默認情況下 spring-boot-data-redis
為我們提供了StringRedisTemplate
但是滿足不了其它類型的轉換,所以還是得自己去定義其它類型的模板….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
package com.battcn.limiter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
|
Limit 攔截器(AOP)
熟悉 Redis
的朋友都知道它是線程安全的,我們利用它的特性可以實現分布式鎖、分布式限流等組件,在一起來學SpringBoot | 第二十三篇:輕松搞定重復提交(分布式鎖)中講述了分布式鎖的實現,限流相比它稍微復雜一點,官方雖然沒有提供相應的API,但卻提供了支持 Lua 腳本的功能,我們可以通過編寫 Lua 腳本實現自己的API,同時他是滿足原子性的….
下面核心就是調用 execute
方法傳入我們的 Lua 腳本內容,然后通過返回值判斷是否超出我們預期的范圍,超出則給出錯誤提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
package com.battcn.limiter;
import com.battcn.limiter.annotation.Limit; import com.google.common.collect.ImmutableList; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest; import java.io.Serializable; import java.lang.reflect.Method;
|