写在前面的话
- 高并发的三驾马车:缓存、降级、限流,这里仅仅说限流
- 常用的限流算法有:计数器算法、固定窗口算法、滑动窗口算法、漏桶算法、令牌桶算法;每种算法的特点和优缺点这里不展开,比较适用的限流算法基本都会选择令牌桶,并且这里基于spring cloud gateway redis本身默认就是基于令牌桶算法实现
- 限流按照类型分为:单机、分布式;
- 限流按照请求流量的路径分为:nginx、gateway、微服务
- 如果仅仅使用于单机环境:谷歌guava的ratelimiter、(atomicinteger、semaphore)【aqs】都是可以选择的; 但是在说到高并发基本已经是分布式环境,此时的常用方案可以基于nginx的ngx_http_limit_req_module模块、 gateway层基于 redis(底层使用lua脚本)、阿里的sentinel(需要单独搭建服务)等方案
- 该方案选择使用spring cloud gateway requestratelimiter进行限流,可选择的策略有:基于访问的ip进行控制,基于访问的请求参数进行控制(前提是接口需要有对应的访问参数),基于actuator实时监控进行控制(比如服务器的cpu达到80%),基于hystrix配置的策略进行控制等
当前选择的方案在是 spring cloud gateway层使用 redis作为分布式的协作中心,默认底层使用令牌桶方式实现,一定要清楚,当前在这个位置:

如果再细分一下,当前位于gateway的内部的这个位置:

实现(这个省略了gateway的搭建和配置等):
1、在gateway网关服务进入redis的pom
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis-reactive</artifactid>
<version>2.3.2.release</version>
</dependency>2、yml配置
spring:
application:
name: kevin-gateway
redis:
host: 82.156.54.7
port: 6379
password: 123456
cloud:
nacos:
discovery:
server-addr: 82.156.54.7:8848
gateway:
globalcors:
cors-configurations:
'[/**]':
allowcredentials: true
allowedorigins: "*"
allowedmethods: "*"
allowedheaders: "*"
add-to-simple-url-handler-mapping: true
# 默认过滤器(对所有route均生效)
default-filters:
# 请求限速配置
- name: requestratelimiter
args:
# 如果keyresolver返回空key,则拒绝该请求403,默认true表示拒绝,false则表示允许访问
deny-empty-key: false
# 令牌桶算法每秒补充的token数量(每秒的请求数量)spring-boot-starter-data-redis-reactive
redis-rate-limiter.replenishrate: 10
# 令牌桶算法token最大数量(每秒的最大请求数量)
redis-rate-limiter.burstcapacity: 15
# 单次请求消费的token数量
redis-rate-limiter.requestedtokens: 10
# 自定义的keyresolver(从请求exchange解析id,用于区分限流的独立单元,如用户id、remoteaddr、sessionid等)
key-resolver: "#{@ipkeyresolver}"
routes:
- id: mosty-base
uri: lb://mosty-base
predicates:
- path=/mosty-base/**
filters:
- stripprefix=1
- name: requestratelimiter
args:
redis-rate-limiter.replenishrate: 10
redis-rate-limiter.burstcapacity: 20
redis-rate-limiter.requestedtokens: 10
key-resolver: "#{@ipkeyresolver}"
- id: mosty-search
uri: lb://mosty-search
predicates:
- path=/mosty-search/**
filters:
- stripprefix=2如上,首先需要进入redis的配置

其次配置限流的filter配置信息,允许配置全局过滤器对所有的route生效,也可以根据需求对每个route进行单独配置


配置参数说明:
- name必须写 requestratelimiter
args参数:
- redis-rate-limiter.replenishrate:发送令牌的速率
- redis-rate-limiter.burstcapacity: 令牌桶的容量
- reids-rate-limiter.requestedtokens: 每个请求耗费的令牌数
- key-resolver:如上所示是配置的一个spring bean的名称,如果没有配置则会获取到keyresolver的默认实现principalnamekeyresolver,并且访问接口都会返回 http 403状态码(与下面的deny-empty-key值相关)
- deny-empty-key: false 如果keyresolver返回空key,则拒绝该请求403,默认true表示拒绝,false则表示允许访问
1、ip限流策略
yml中配置 keyresolver:"#{@ipkeyresolver}"
@bean(name = "ipkeyresolver")
public keyresolver ipkeyresolver() {
return new keyresolver() {
@override
public mono<string> resolve(serverwebexchange exchange) {
string hostname = objects.requirenonnull(exchange.getrequest()
.getremoteaddress()).gethostname();
system.out.println("hostname:" + hostname);
return mono.just(hostname);
}
};
}基于限流策略,正常访问的效果,以及被限流的效果(返回标准的http 429编码,too many request)

2、请求参数限流策略
yml中配置 keyresolver:"#{@userkeyresolver}"
@bean
public keyresolver userkeyresolver() {
return exchange -> mono.just(objects.requirenonnull(exchange.getrequest().getqueryparams().getfirst("userid")));
}在下面的微服务的接口中一定要有该参数,即需要能在请求参数中获取到该值
@getmapping("/getusernamebyuserid")
public string userinfo(@requestparam("userid") string userid) {
// 查询数据库获取
return "user name of" + userid;
}3、请求路径(即接口)限流
yml中配置 keyresolver:"#{@requestpathkeyresolver}"
@bean("requestpathkeyresolver")
public keyresolver requestpathkeyresolver() {
return exchange -> mono.just(objects.requirenonnull(
exchange.getrequest().geturi().()));
}4、基于hystrix熔断进行限流策略
基于上面的引入pom:spring-boot-starter-data-redis-reactive外,还需要引入
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-netflix-hystrix</artifactid>
</dependency>将hystrix配置为全局的过滤器(对所有的 route生效),如下
说明:
name执行过滤器的类型,指向了 hystrix过滤器
args:default-filters的hystrix将会使用hystrixcommand打包剩余的过滤器,并命名为fallbackcmd,我们还配置了可选的参数fallbackuri,降级逻辑被调用,请求将会被转发到uri为/fallbackcontroller的控制器处理
spring:
cloud:
gateway:
# 默认过滤器(对所有route均生效)
default-filters:
- name: hystrix
args:
name: fallbackcmd
fallbackuri: forward:/fallbackcontroller
添加 hystrix的fallback的控制器接口方法

此时需要添加一个fallback的接口,并且返回想要的数据结构
@responsebody
@requestmapping(value = "/fallbackcontroller")
public responseresult<object> fallbackcontroller() {
return responseresult.fail(httpstatus.too_many_requests.value(), "超时限流", null);
}生效还需要配置hystrix的超时时间(yml配置如下):
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutinmilliseconds

并且记得在启动类上添加hystrix的启动项
@enablehystrix
为了实现效果此时将待访问的接口直接进行sleep 5s,测试效果
@slf4j
@restcontroller
public class testcontroller {
@requestmapping("/test/fallback")
public object fallacak() throws interruptedexception {
thread.sleep(5000);
log.info("熔断处理!!!");
return "service error!!!";
}
}总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论