微服务网关构建之限流防刷

微服务网关限流防刷功能开发流程

一、网关是什么?

网关(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对于项目参数在启动时进行动态修改,更符合实际生产需求。

以上就是限流防刷功能的相关开发流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

J_Silver

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值