1、前言
作为一个后端开发,对于后端服务的安全性方面,一定要有足够的考虑。近期的开发工作中,有一个实现分享外部链接的需求点,个人认为这一块会有安全隐患。比如,因为这个分享的外链会被用户无限制点开查看,那如果有人恶意点击,或高频调用,那这种情况可能会对我们的后端服务造成一定的隐患问题。所以,防患于未然,今天考虑做个限流处理的方案。
2、限流实现
2.1 限流方式
限流方式有多种,例如Nginx、zuul、springcloud-gateway,Google的guava,或是应用服务AOP内。
2.2 限流算法
限流算法常用的有4种:固定窗口限流算法、滑动窗口限流算法、漏桶算法、令牌桶算法
2.3 springcloud-gateway内部自带限流实现
引入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!--基于 reactive stream 的redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
注入限流维度的bean:
package xxx.xxx.xxx.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import reactor.core.publisher.Mono;
@EnableDiscoveryClient
@SpringBootApplication
@ComponentScan(value = "xxx.xxx.xxx")
public class GatewayStartup {
public static void main(String[] args) {
SpringApplication.run(GatewayStartup.class, args);
}
@Bean(name = "remoteAddrKeyResolver")
public KeyResolver remoteAddrKeyResolver() {
//按请求ip限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
}
在yml中设置路由限流的配置:
spring:
cloud:
gateway:
routes: #限流路由配置
- id: requestLimiter
uri: lb://testService #路由的服务名称
predicates:
- Path= /context/testLimit/** #需要限流的接口路径
filters:
- name: RequestRateLimiter #过滤器名称
args:
key-resolver: "#{@remoteAddrKeyResolver}" #使用SpEL表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象
redis-rate-limiter.replenishRate: 20 # 令牌桶填充的速率 秒为单位
redis-rate-limiter.burstCapacity: 20 # 令牌桶总容量
redis-rate-limiter.requestedTokens: 1 # 每次请求获取的令牌数
redis:
host: localhost
port: 6379
对限流路径下的一个接口,通过压测工具,进行多线程压测一番,看看实践演示效果:
我这里使用的压测工具是ApiFox,也可以使用Jmeter之类的,下面设置5个线程,每个线程循环请求5次
通过测试报告可以看到:
再测一次:
其实,gateway这里主要是依赖于redis来实现的令牌桶的限流处理,我们可以查看redis内的限流key情况:
到此,基本限流就实现完成啦!!!
3、总结
在实践过程中,有2个点需要注意:
①第一点,yml中的配置一定要注意缩进格式,以及path路径的配置(我一开始就是路径少写了,导致没生效;缩进不对会导致启动报错)
②第二点,redis中的限流存储的key值,只有在请求执行过程中才会查看的到,请求结束后不一会儿,key就会被删除,看不到这个key值啦(刚开始我去查询key值就是在执行结束之后,一直看不到key值效果)
最后,对于限流的配置数值,其实也没有一个权威的限制标准,所以需要根据自己的服务能力以及平常业务的正常TPS值来做一下参考,一般设置一个高于平常业务的TPS值,应该就差不多了。因为个人认为,一般只有恶意请求或是攻击请求,才有可能会比自己设置的阈值高出很多。
若有任何不对之处,欢迎下方留言探讨~~~