这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
前言
GateWay 网关,在微服务中为了方便统一管理且对外暴露服务端地址,起到了内部和外部的隔离保证了后台服务的安全性。之外还是可以实现 限流的(Url,参数,Ip)都可以进行限流。今天我们就来使用一下,并了解一下当前比较流行的限流采用的方式都有哪些
限流算法
计数器算法:
这种算法是限流中最简单最容易实现的算法当然他也存在一个问题就是边界值,我们定制规则A接口一分钟之内只能请求100次 ,那么当我们在 59秒的时候一下子将100个请求拉完,到了1分之后再拉100个请求。这样等于我们的限流失效了并且在边界值时会出现双倍流量涌入,而且存在资源利用不合理 在之前 58秒都没有请求。
漏桶(leaky bucket)算法:
我们可以按照字面意思理解,一个桶子(容器)注满水或者任意速度注入(请求)这个桶子下面是有一个口,可以让水以一定的速度往出流水,溢出时则丢弃,这个算法的是可以使用队列实现的。可以将突发流量进行整形(即时流量很大但是溢出的都会被丢弃从而保证了下游服务正常使用但是自身服务的压力过大可能会造成大量的请求堆压),因为桶子的大小是固定的而且流出的速率也是一定的。且不会存在边界值的问题。
令牌桶算法: 可以理解为 漏桶算法的一个升级改进漏桶算法能欧限制请求调用的速率,而令牌桶的能够在限制调用平均速率的同时还能初一突发流量调用。当令牌有100个时一次请求进来1000个会从第1001个开始可能会失败但是下一个令牌已经生成,令牌以恒定的速度生成。不会将后面的所有请求全部拒绝掉。只有请求获取到令牌才会往下继续走,否则会等待或者直接拒绝(当然也和你的业务场景去选择)
springCloud GateWay 官方提供了
RequestRateLimiterGatewayFilterFactory 过滤器工厂使用Redis 和Lua脚本实现的令牌桶的方式
下面我们使用一下
首先引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
添加配置文件
spring:
application:
name: bugvip-gatway
profiles:
active: dev
redis:
host: 192.168.1.7
prot: 6379
password: bugvipxk
cloud:
nacos:
discovery:
server-addr: 192.168.1.7:8848
gateway:
routes:
- id: bugvip-oss-image
uri: lb://bugvip-oss-image
predicates:
- Path=/api/third/**
filters:
- RewritePath=/api/(?<segment>.*),/${segment}
- name: RequestRateLimiter #这个是固定的写法
args:
redis-rate-limiter.replenishRate: 1 #这个是生成令牌的速率每秒1个
redis-rate-limiter.burstCapacity: 2 # 令牌总数
key-resolver: "#{@pathKeyResolver}" #这个是我们配置的规则 使用spel表达式引用
添加配置类
这个是通过URL进行限流
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver(){
//这个是通过URL进行限流
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
下面我我们使用postman测试一下是否生效
可以看到频繁请求后 返回给我们
HTTP 429 - Too Many Requests(当然这个是默认的)
通过IP
@Bean
public KeyResolver addressKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
参数进行限流的
@Bean
public KeyResolver queryKeyResolver(){
//注意这里的getFirst并不是第一个的意思而是所有的参数都可以通过这个方法获取
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("hit"));
}
只需要替换 key-resolver的值为下面的bean 名称就可以了。
实践是检验真理的唯一准则,感兴趣的可以去试试呀!明天见咯 😃😃😃😃