【教程】SpringCloud+Nacos+Feign+Gateway ( 八 ) Nacos-Gateway 网关 限流
从某种意义上讲,令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

1 增加依赖
<!-- 限流Redis实现 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>com.gw</groupId>
<artifactId>gwcloud-starter-redis</artifactId>
</dependency>
2 增加配置application.yml
核心配置
# 连接Redis配置
redis:
host: redis-server-02
port: 6379
database: 0
password: ****** filters:
# 熔断器
- name: Hystrix
args:
name: fallbackCmd
fallbackUri: 'forward:/fallbackCmd'
## 限流
- name: RequestRateLimiter
args:
### 限流过滤器的 Bean 名称 #用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象 对应com.gw.module.gateway.config.RateLimiterConfiguration.apiKeyResolver
key-resolver: '#{@apiKeyResolver}'
### 希望允许用户每秒处理多少个请求 #令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
### 用户允许在一秒钟内完成的最大请求数 #令牌桶总容量
redis-rate-limiter.burstCapacity: 100完整配置
server:
# 端口
port: 9999
spring:
application:
# 应用名称
name: gwcloud-module-gateway
# 连接Redis配置
redis:
host: redis-server-02
port: 6379
database: 0
password: ******
cloud:
# 网关配置
gateway:
discovery:
locator:
# 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
# 开启从注册中心动态创建路由的功能
enabled: true
globalcors:
cors-configurations:
'[/**]':
allowCredentials: true
allowedOrigins: "*"
allowedMethods: "*"
allowedHeaders: "*"
#如果启用nacos或者数据库配置请删除以下配置
# 路由(routes:路由,它由唯一标识(ID)、目标服务地址(uri)、一组断言(predicates)和一组过滤器组成(filters)。filters 不是必需参数。)
routes:
#路由标识(id:标识,具有唯一性) 简单尝试
- id: gwcloud-user-provide-service
# 目标服务地址(uri:地址,请求转发后的地址) lb://服务名
# lb协议表示启用负载均衡功能,然后后面跟着微服务名称
uri: lb://gwcloud-user-provide-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 转发地址格式为 uri/nacos-config/**
- Path=/nacos-config/**
# 转发地址格式为 uri/nacos-config
# - Path=/nacos-config
filters:
# 熔断器
- name: Hystrix
args:
name: fallbackCmd
fallbackUri: 'forward:/fallbackCmd'
## 限流
- name: RequestRateLimiter
args:
### 限流过滤器的 Bean 名称 #用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象 对应com.gw.module.gateway.config.RateLimiterConfiguration.apiKeyResolver
key-resolver: '#{@apiKeyResolver}'
### 希望允许用户每秒处理多少个请求 #令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
### 用户允许在一秒钟内完成的最大请求数 #令牌桶总容量
redis-rate-limiter.burstCapacity: 100
#路由标识(id:标识,具有唯一性) 简单尝试
- id: gwcloud-user-provide-service
# 目标服务地址(uri:地址,请求转发后的地址) lb://服务名
uri: lb://gwcloud-user-provide-service
# 路由条件(predicates:断言,匹配 HTTP 请求内容)
predicates:
## 通过请求参数过滤
- Query=token
# 全局 filters
default-filters:
# 全局熔断降级配置
- name: Hystrix
args:
name: default
### fallback 时调用的方法 http://localhost:9999/fallback
fallbackUri: 'forward:/fallback'
# 这种熔断只能针对服务网关调用后端服务接口超时才会生效,后端服务抛异常无效
# hystrix 信号量隔离, default-全局默认-60秒后自动超时
hystrix:
enabled: true
shareSecurityContext: true
command:
default: # 默认
execution:
isolation:
# 隔离策略 THREAD以及SEMAPHORE
# THREAD:当隔离策略为 THREAD 时,是没办法拿到 ThreadLocal 中的值的
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 60000
fallbackCmd:
execution:
isolation:
# strategy: SEMAPHORE
thread: # 60秒没响应自动熔断 跳转到配置的fallbackUri
timeoutInMilliseconds: 60000
3.核心代码
package com.gw.module.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;
/**
* @Title: 限流配置类
* @Description: 描述
* @Version: v1.0
* @Author: Mr.Guan
* @Mail GuanWeiMail@163.com
* @DateTime: 2021-03-08
* @Project springcloud-gw-parent
* @Package com.gw.module.gateway.config
*/
@Configuration
public class RateLimiterConfiguration {
/**
* IP限流 (通过exchange对象可以获取到请求信息,这边用了HostName)
*/
@Bean
@Primary
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
/**
* 用户限流 (通过exchange对象可以获取到请求信息,获取当前请求的用户 TOKEN)
*/
@Bean
public KeyResolver userKeyResolver() {
//使用这种方式限流,请求Header中必须携带X-Access-Token参数
return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst("GlobalAccessTokenFilter.X_ACCESS_TOKEN"));
}
/**
* 接口限流 (获取请求地址的uri作为限流key)
*/
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
/**
* 参数限流
* @return
*/
@Bean
public KeyResolver paramsKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
}
4 测试 借助 Jmeter
参考:https://blog.csdn.net/G971005287W/article/details/105258275
https://blog.csdn.net/G971005287W/article/details/114537942

64%的请求被拦截

版权声明:本文为G971005287W原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。