一、网关是什么?
网关(Gateway)是网络架构中的一个关键组件,它充当不同网络或系统之间的桥梁和入口,用于管理和控制数据流。网关通常负责请求的接收与转发、协议的转换、安全认证、流量控制等功能,是连接内部服务与外部客户端的“守门员”。在微服务架构中,尤其是在Spring Cloud的生态系统中,网关的作用更加重要,它能够简化请求路由、统一管理认证和限流,确保系统的安全性和稳定性。网关在Spring Cloud微服务中通常扮演以下功能角色:请求路由(Routing)、负载均衡(Load Balancing)、统一日志与监控(Logging & Monitoring)、请求过滤(Filtering)。这其中限流防刷就属于请求过滤中的重要一环。
二、为什么需要限流防刷?
在微服务架构中,随着系统规模的扩大和并发请求量的增加,如何确保服务的稳定性和安全性成为了至关重要的问题。特别是在面对高并发请求或恶意刷请求的情况下,系统很容易因为资源消耗过快而崩溃或性能下降。为了解决这些问题,限流防刷机制逐渐成为微服务网关中不可或缺的一部分。通过在网关层实现限流策略,不仅可以有效防止恶意请求对后端服务的冲击,还能合理分配系统资源,提升整体服务的稳定性和用户体验。例如臭名昭著的DDoS攻击,限流防刷可作为应对攻击的第一道屏障。
三、限流防刷的具体实现原理
限流防刷是一种在系统层面上控制请求频率和流量的策略,旨在防止单个用户或客户端发送过多请求。这里我们主要针对发起请求的ip地址做记录与限制。

目前我的服务搭建如上,通过网关对api模块中的三个服务进行分发管理。首先在网关模块下/src/main/java/org/example下创建filter包,创建IPLimitFilter类:
@Component //声明为spring Bean
@Slf4j //日志记录
@RefreshScope //通过SpringCloudConfigServer动态刷新配置时自动刷新(后面通过nacos实现)
public class IPLimitFilter extends BaseInfoProperties implements GlobalFilter, Ordered {
/**
* 需求:
* 判断请求的ip在20秒内的请求次数是否超过3次
* 超过3次则限制访问30s
* 等待30s静默后才能恢复访问
*/
private static final Integer continueCounts;
private static final Integer timeInterval;
private static final Integer limitTimes;
}
根据需求,我们不难发现,需要对同一ip进行多次记录以及计时,使用Redis的INCR命令对请求计数,并结合EXPIRE命令设置计数器的过期时间,以实现固定时间窗口的限流。所以这里继承Redis实现类BaseInfoProperties。
实现的两个接口中GlobalFilter是 Spring Cloud Gateway 中的一个接口,用于定义全局过滤器。全局过滤器可以在所有通过网关的请求上执行逻辑。用于在请求进入网关时(或响应从网关返回时)进行统一处理,比如鉴权、日志记录、修改请求或响应内容等。filter(ServerWebExchange exchange, GatewayFilterChain chain)是 GlobalFilter 接口中的唯一方法。它定义了当请求到达网关时,过滤器应该如何处理请求。(ServerWebExchange:封装了 HTTP 请求和响应的信息。GatewayFilterChain:用于将请求传递到下一个过滤器。)通常会调用 chain.filter(exchange) 方法,将处理后的请求交给下一个过滤器进行处理。
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("IPLimitFilter当前的执行顺序order为1");
return doLimit(exchange,chain); //限流具体实现方法
}
Ordered接口用于定义过滤器的执行顺序。
@Override
public int getOrder() {
return 1; //返回数字越小优先级越高
}
接下来就是对限流具体逻辑方法的实现:
public Mono<Void> doLimit(ServerWebExchange exchange,GatewayFilterChain chain){
//根据request请求获得ip
ServerHttpRequest request = exchange.getRequest();
String ip = IPUtil.getIP(request);
//ip定义
final String ipRedisKey = "gateway-ip:" + ip;
//拦截的黑名单ip若存在于redis中,则是被关小黑屋
final String ipRedisLimitKey = "gateway-ip:limit" + ipRedisKey;
//获得当前ip并查询还剩下多长小黑屋时间
long limitLeftTimes = redis.ttl(ipRedisLimitKey);
if(limitLeftTimes>0){
return renderErrorMsg(exchange,ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
}
//在redis中获得ip的累加次数
long requestCounts = redis.increment(ipRedisKey,1);
//判断第一次进来,设置间隔时间
if(requestCounts==1){
redis.expire(ipRedisKey,timeInterval);
}
//一旦请求次数超过限定的访问次数,则需要限定当前ip
if (requestCounts>continueCounts){
redis.set(ipRedisKey,ipRedisLimitKey,limitTimes);
return renderErrorMsg(exchange,ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
}
return chain.filter(exchange); //交由下一级过滤器处理
}
还有一点补充,关于前端的消息返回:
public Mono<Void> renderErrorMsg(ServerWebExchange exchange, ResponseStatusEnum statusEnum){
//获得相应请求
ServerHttpResponse response = exchange.getResponse();
//构建jsonResult
GraceJSONResult jsonResult = GraceJSONResult.exception(statusEnum);
//设置header类型
if (!response.getHeaders().containsKey("Content-Type")){
response.getHeaders().add("Content-Type", MimeTypeUtils.APPLICATION_JSON_VALUE);
}
//修改response的状态码code为500
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
//转换json并且向response中写入数据
String resultJson = new Gson().toJson(jsonResult);
DataBuffer buffer = response.bufferFactory().wrap(resultJson.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
随后我们刷新maven并进行install操作,build success后启动服务。
对任意一个功能模块进行访问:

访问成功,通过Redis桌面端可视化软件RESP可以看到ip被记录,以及时间段内的请求次数:

如果在规定时间内访问超过限制次数则会显示如下:

Redis存储内容如下:

四、通过nacos动态编辑限流参数
需要本地安装nacos,并在项目中引入相关依赖bootstrap。
服务需要在nacos进行注册,网关模块的yaml配置文件相关内容如下:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# username: nacos
# password: nacos
gateway:
discovery:
locator:
enabled: true #动态路由
routes:
- id: authRoute
uri: lb://auth-service #lb=负责均衡,动态寻址
predicates:
- Path=/a/**
- id: fileRoute
uri: lb://file-service
predicates:
- Path=/f/**
- id: mainRoute
uri: lb://main-service
predicates:
- Path=/m/**
globalcors:
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
通过访问127.0.0.1:8848本地设定端口访问,创建配置如下(可参考nacos官方文档):

对IPLimitFilter的参数进行修改:
@Value("${blackIP.continueCounts}")
private Integer continueCounts;
@Value("${blackIP.timeInterval}")
private Integer timeInterval;
@Value("${blackIP.limitTimes}")
private Integer limitTimes;
项目启动过程中,可以随时在nacos服务中心修改配置并发布。
这样通过nacos对于项目参数在启动时进行动态修改,更符合实际生产需求。
以上就是限流防刷功能的相关开发流程。
微服务网关限流防刷功能开发流程
8571

被折叠的 条评论
为什么被折叠?



