網關可以做很多的事情,比如,限流,當我們的系統被頻繁的請求的時候,就有可能 將系統壓垮,所以 爲了解決這個問題,需要在每一個微服務中做限流操作,但是如果有了網關,那麼就可以在網關係統做限流,因爲所有的請求都需要先通過網關係統才能路由到微服務中。
令牌桶算法
令牌桶算法是比較常見的限流算法之一,大概描述如下:
1)所有的請求在處理之前都需要拿到一個可用的令牌纔會被處理;
2)根據限流大小,設置按照一定的速率往桶裏添加令牌;
3)桶設置最大的放置令牌限制,當桶滿時、新添加的令牌就被丟棄或者拒絕;
4)請求達到後首先要獲取令牌桶中的令牌,拿着令牌纔可以進行其他的業務邏輯,處理完業務邏輯之後,將令牌直接刪除;
5)令牌桶有最低限額,當桶中的令牌達到最低限額的時候,請求處理完之後將不會刪除令牌,以此保證足夠的限流
如下圖:
這個算法的實現,有很多技術,Guaua是其中之一,redis客戶端也有其實現。
網關限流代碼實現
(1)spring cloud gateway 默認使用redis的RateLimter限流算法來實現。所以我們要使用首先需要引入redis的依賴 (這裏需要使用響應式的redis依賴spring-boot-starter-data-redis-reactive)
<dependencies>
<!--網管-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--熔斷器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--redis用於實現限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>
(2)定義KeyResolver
在GatewayApplicatioin引導類中添加如下代碼,KeyResolver用於計算某一個類型的限流的KEY也就是說,可以通過KeyResolver來指定限流的Key。
package com.changgou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author :gzy
* @date :Created in 2019/8/15
* @description :
* @version: 1.0
*/
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class);
}
@Bean
//KeyResolver用於計算某一個類型的限流的KEY也就是說,可以通過KeyResolver來指定限流的Key。
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
}
(3)application.yml配置
spring:
application:
name: gateway
cloud:
gateway:
routes:
#與微服務名稱對應
- id: goods
# 路由地址(轉發地址),這裏根據服務名稱,採用lb協議,會從Eureka註冊中心獲取服務請求地址
# 路由地址如果通過lb協議加服務名稱時,會自動使用負載均衡訪問對應服務
# 規則:lb協議+服務名稱
uri: lb://goods
predicates:
# 路由攔截地址(斷言),請求路徑要加goods,後面攔截器會自動去掉
- Path=/goods/**
#訪問地址自動去掉一個前綴
filters:
- StripPrefix= 1
- id: system
uri: lb://system
predicates:
- Path=/system/**
filters:
- StripPrefix= 1
- name: RequestRateLimiter #請求數限流 名字不能隨便寫
args:
key-resolver: "#{@ipKeyResolver}"
redis-rate-limiter.replenishRate: 1 #同一個IP在一秒中只能訪問一次
redis-rate-limiter.burstCapacity: 1 #在突發情況下 一個IP在一秒中只能訪問一次
redis:
host: 192.168.200.128
server:
port: 9101
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka
instance:
prefer-ip-address: true
解釋:
- burstCapacity:令牌桶總容量。
- replenishRate:令牌桶每秒填充平均速率。
- key-resolver:用於限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據#{@beanName}從 Spring 容器中獲取 Bean 對象。
通過在replenishRate和中設置相同的值來實現穩定的速率burstCapacity。設置burstCapacity高於時,可以允許臨時突發replenishRate。在這種情況下,需要在突發之間允許速率限制器一段時間(根據replenishRate),因爲2次連續突發將導致請求被丟棄(HTTP 429 - Too Many Requests)
key-resolver: "#{@userKeyResolver}" 用於通過SPEL表達式來指定使用哪一個KeyResolver.
如上配置:
表示 一秒內,允許 一個請求通過,令牌桶的填充速率也是一秒鐘添加一個令牌。
最大突發狀況 也只允許 一秒內有一次請求,可以根據業務來調整 。