spring cloud gateway默認基於redis令牌桶算法進行微服務的限流保護,采用RateLimter限流算法來實現。
1.引入依賴包
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2、yml中配置redis
spring:
application:
name: mima-cloud-gateway
redis:
database: 1
host: localhost
port: 6379
password:
3、配置KeyResolver——RateLimiteConfig.java(接口限流/ip限流/用戶限流)
package com.mkevin.gateway.config; import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import reactor.core.publisher.Mono; /** * 限流配置KeyResolver——有三種寫法(接口限流/ip限流/用戶限流) */ @Configuration public class RateLimiteConfig { /** * 接口限流:根據請求路徑限流 * @return
*/
/* 如果不使用@Primary注解,會報如下錯誤,需要注意 Description: Parameter 1 of method requestRateLimiterGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a single bean, but 3 were found: - pathKeyResolver: defined by method 'pathKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class] - ipKeyResolver: defined by method 'ipKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class] - userKeyResolver: defined by method 'userKeyResolver' in class path resource [com/mkevin/gateway/config/RateLimiteConfig.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed */ @Bean @Primary public KeyResolver pathKeyResolver() { //寫法1
return exchange -> Mono.just( exchange.getRequest() .getPath() .toString() ); /* //寫法2 return new KeyResolver() { @Override public Mono<String> resolve(ServerWebExchange exchange) { return Mono.just(exchange.getRequest() .getPath() .toString()); } }; */ } /** * 根據請求IP限流 * @return
*/ @Bean public KeyResolver ipKeyResolver() { return exchange -> Mono.just( exchange.getRequest() .getRemoteAddress() .getHostName() ); } /** * 根據請求參數中的userId進行限流 * * 請求地址寫法:http://localhost:8801/rate/123?userId=lisi * * @return
*/ @Bean public KeyResolver userKeyResolver() { return exchange -> Mono.just( exchange.getRequest() .getQueryParams() .getFirst("userId") ); } }
4、yml中配置spring.cloud.gateway.routes.filters
spring: cloud: gateway: routes: - id: rate-limit-demo uri: lb://mima-cloud-producer
predicates: #訪問路徑:http://localhost:8801/rate/123
- Path=/rate/** filters: - name: RequestRateLimiter args: # 令牌桶每秒填充平均速率, 允許用戶每秒處理多少個請求。 redis-rate-limiter.replenishRate: 1 # 令牌桶的容量,允許在1s內完成的最大請求數。 redis-rate-limiter.burstCapacity: 2 # 使用SpEL表達式從Spring容器中獲取Bean對象, 查看RateLimiteConfig實現類中的方法名 key-resolver: "#{@pathKeyResolver}" #key-resolver: "#{@ipKeyResolver}" #key-resolver: "#{@userKeyResolver}"
5、訪問地址測試
http://localhost:8801/rate/123
當F5頻繁刷新請求接口時,控制台會報429錯誤狀態碼,提示我們請求過多,如下:
[開始]請求路徑:/rate/123 [應答]請求路徑:/rate/123耗時:2ms 2020-09-08 16:23:27.253 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] Completed 429 TOO_MANY_REQUESTS 2020-09-08 16:23:27.394 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] HTTP GET "/rate/123" corsFilter... run [開始]請求路徑:/rate/123 [應答]請求路徑:/rate/123耗時:2ms 2020-09-08 16:23:27.397 DEBUG 18512 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] Completed 429 TOO_MANY_REQUESTS 2020-09-08 16:23:27.536 DEBUG 18512 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter : [62eb90e0] HTTP GET "/rate/123" corsFilter... run
6、當發生限流時,會向redis中存儲兩個數據
127.0.0.1:1>keys *
1) "request_rate_limiter.{/rate/123}.timestamp"
2) "request_rate_limiter.{/rate/123}.tokens"
127.0.0.1:1>keys *
1) "request_rate_limiter.{0:0:0:0:0:0:0:1}.timestamp"
2) "request_rate_limiter.{0:0:0:0:0:0:0:1}.tokens"
127.0.0.1:1>keys *
1) "request_rate_limiter.{lisi}.timestamp"
2) "request_rate_limiter.{lisi}.tokens"
參數說明:
request_rate_limiter.{key}.timestamp:
存儲的是當前時間的秒數,也就是System.currentTimeMillis()/1000或者Instant.now().getEpochSecond()。
request_rate_limiter.{key}.tokens:
存儲的是當前這秒鍾對應的可用令牌數量
7、完整yml配置文件
# 開啟resilience4j斷路器 # spring.cloud.circuitbreaker.resilience4j.enabled: true # 設置hystrix斷路器超時時間 # hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 2000 spring: application: name: mima-cloud-gateway redis: database: 1 host: localhost port: 6379 password: cloud: gateway: routes: - id: rate-limit-demo uri: lb://mima-cloud-producer
predicates: #訪問路徑:http://localhost:8801/rate/123
- Path=/rate/** filters: - name: RequestRateLimiter args: # 令牌桶每秒填充平均速率, 允許用戶每秒處理多少個請求。 redis-rate-limiter.replenishRate: 1 # 令牌桶的容量,允許在1s內完成的最大請求數。 redis-rate-limiter.burstCapacity: 2 # 使用SpEL表達式從Spring容器中獲取Bean對象, 查看RateLimiteConfig實現類中的同名方法 #key-resolver: "#{@pathKeyResolver}" #key-resolver: "#{@ipKeyResolver}" #請求地址寫法:http://localhost:8801/rate/123?userId=lisi key-resolver: "#{@userKeyResolver}" # The RequestRateLimiter GatewayFilter Factory # The Redis RateLimiter # Modify a Request Body GatewayFilter Factory 測試版本,未來可能改動 # Modify a Response Body GatewayFilter Factory 測試版本,未來可能改動 server: port: 8801 eureka: client: serviceUrl: #defaultZone: http://kevin:123456@localhost:8761/eureka/ defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true #instance-id: ${spring.application.name}:${spring.cloud.client.ip-address:}:${server.port} instance-id: ${spring.application.name}:${server.port} debug: true management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always shutdown: true