1.什么是網關限流?

2.為什么需要限流

3.限流算法
做限流 (Rate Limiting/Throttling) 的時候,除了簡單的控制並發,如果要准確的控制 TPS,簡單的做法是維護一個單位時間內的 Counter,如判斷單位時間已經過去,則將 Counter 重置零。此做法被認為沒有很好的處理單位時間的邊界,比如在前一秒的最后一毫秒里和下一秒的第一毫秒都觸發了最大的請求數,也就是在兩毫秒內發生了兩倍的 TPS。
常用的更平滑的限流算法有兩種:漏桶算法和令牌桶算法。很多傳統的服務提供商如華為中興都有類似的專利,參考采用令牌漏桶進行報文限流的方法。
3.1 漏桶算法
漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶里,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(訪問頻率超過接口響應速率),然后就拒絕請求,可以看出漏桶算法能強行限制數據的傳輸速率。
可見這里有兩個變量,一個是桶的大小,支持流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate)。因為漏桶的漏出速率是固定的參數,所以,即使網絡中不存在資源沖突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率。因此,漏桶算法對於存在突發特性的流量來說缺乏效率。

3.2 令牌桶算法
令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易理解。隨着時間流逝,系統會按恆定 1/QPS 時間間隔(如果 QPS=100,則間隔是 10ms)往桶里加入 Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了。新請求來臨時,會各自拿走一個 Token,如果沒有 Token 可拿了就阻塞或者拒絕服務。
令牌桶的另外一個好處是可以方便的改變速度。一旦需要提高速率,則按需提高放入桶中的令牌的速率。一般會定時(比如 100 毫秒)往桶中增加一定數量的令牌,有些變種算法則實時的計算應該增加的令牌的數量。Guava 中的 RateLimiter 采用了令牌桶的算法,設計思路參見 How is the RateLimiter designed, and why?,詳細的算法實現參見源碼。

4.GateWay限流

4.1 添加依賴
<!-- commons-pool2對象池依賴-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--RequestRateLimiter限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
4.2 添加redis配置
spring: redis: host: 106.14.24.156 port: 6380 password: dm_webapi database: 0 timeout: 20s # 連接超時時間(毫秒)默認是2000ms lettuce: pool: max-active: 1024 # 連接池最大連接數(使用負值表示沒有限制) max-wait: -1s # 連接池最大阻塞等待時間(使用負值表示沒有限制) max-idle: 200 # 連接池中的最大空閑連接 min-idle: 5 # 連接池中的最小空閑連接
4.3 配置類配置限流規則
@Configuration public class KeyResolverConfig { @Bean @Primary KeyResolver apiKeyResolver() { //按URL限流,即以每秒內請求數按URL分組統計,超出限流的url請求都將返回429狀態 return exchange -> Mono.just(exchange.getRequest().getPath().toString()); } @Bean KeyResolver userKeyResolver() { //按用戶限流 return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); } @Bean KeyResolver ipKeyResolver() { //按IP來限流 return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); } }
4.2 添加路由限流規則配置
spring: gateway: routes: - id: oneserver-service uri: lb://oneserver-service #lb代表負載均衡 member為注冊中心上的服務名 predicates: - Path=/oneserver/** filters:
#截取調oneserver - StripPrefix=1 # 限流過濾器,使用gateway內置令牌算法 - name: RequestRateLimiter args: # 令牌桶每秒填充平均速率,即行等價於允許用戶每秒處理多少個請求平均數 redis-rate-limiter.replenishRate: 1 # 令牌桶的容量,允許在一秒鍾內完成的最大請求數 redis-rate-limiter.burstCapacity: 2 # 用於限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據#{@beanName}從 Spring 容器中獲取 Bean 對象。 key-resolver: "#{@apiKeyResolver}" discovery: locator: enabled: true lower-case-service-id: true
多次請求 http://localhost:7001/oneserver/getLcoUsers 結果如下:

Redis結果如下:

