【Java-EE进阶】SpringBoot针对某个IP限流问题

目录

简介

[1. 使用Guava的RateLimiter实现限流](#1. 使用Guava的RateLimiter实现限流)

添加Guava依赖

实现RateLimiter限流逻辑

限流管理类

控制器中应用限流逻辑

[2. 使用计数器实现限流](#2. 使用计数器实现限流)

限流管理类

控制器中应用限流逻辑


简介

针对某个IP进行限流以防止恶意点击是一种常见的反爬虫和防止DoS的措施。限流策略通过对某个IP的访问频率进行控制,防止恶意用户对应用造成负面的影响。

以下是实现限流的步骤和方法,在Java后端通常这样实现:

1. 使用Guava的RateLimiter实现限流

Guava库提供了一个简单而高效的限流工具:RateLimiter,可以方便的实现针对IP的访问频率控制。

添加Guava依赖

首先,在pom.xml文件中添加Guava依赖:

复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>
实现RateLimiter限流逻辑
限流管理类

创建一个类来管理针对IP的限流:

复制代码
import com.google.common.util.concurrent.RateLimiter;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class RateLimiterManager {

    private final ConcurrentMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();
    private final double permitsPerSecond = 1.0; // 每秒允许1次请求

    public boolean tryAcquire(String ip) {
        RateLimiter rateLimiter = rateLimiterMap.computeIfAbsent(ip, k -> RateLimiter.create(permitsPerSecond));
        return rateLimiter.tryAcquire();
    }
}
控制器中应用限流逻辑

在Spring Boot控制器中应用限流逻辑:

复制代码
import com.xfusion.rate1.limit.RateLimiterManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
@RequestMapping("/test")
public class TestController {
    private final RateLimiterManager rateLimiterManager=new RateLimiterManager();
    @RequestMapping("/t1")
    public void test1(HttpServletRequest request, HttpServletResponse response) throws IOException {
       String param=request.getRemoteAddr();
       if(rateLimiterManager.tryAcquire(param)) {
          response.getWriter().write("Request processed for " + param);
       } else {
           response.setStatus(HttpServletResponse.SC_FORBIDDEN);
           response.getWriter().write("Request limit for " + param);
        }
    }
}

private final ConcurrentMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>();
  • ConcurrentMap<String, RateLimiter>

    • 使用ConcurrentMap来存储每个IP的限流器(RateLimiter)。
    • ConcurrentMap接口允许高效地进行并发访问和更新,确保线程安全。
  • new ConcurrentHashMap<>()

    • 实例化一个ConcurrentHashMap,它是ConcurrentMap的常用实现。这种数据结构支持线程安全的读写操作,适合限流场景。

      private final double permitsPerSecond = 1.0; // 每秒允许1次请求

  • permitsPerSecond

    • 定义一个double类型的常量permitsPerSecond,值为1.0

    • 表示每秒允许1次请求的限流速率。RateLimiter根据此值来创建相应的限流器。

      RateLimiter rateLimiter = rateLimiterMap.computeIfAbsent(ip, k -> RateLimiter.create(permitsPerSecond));

  • computeIfAbsent

    • computeIfAbsent方法用于检查map中是否已经存在为该IP准备的RateLimiter
    • 若不存在,则使用k -> RateLimiter.create(permitsPerSecond)创建一个新的RateLimiter。这个lambda部分表示为未存在的IP创建一个新的RateLimiter,限流速率为permitsPerSecond
  • RateLimiter.create(permitsPerSecond)

    • 调用RateLimiter类的静态方法create,以指定速率创建一个新的限流器实例。这使得每秒最多处理一个请求。

      return rateLimiter.tryAcquire();

  • rateLimiter.tryAcquire()

    • 尝试获取一个请求许可。在给定的限流速率范围内,如果成功获取许可,则返回true,否则返回false

    • tryAcquire使得在请求达到速率限制时,予以限制,而不使请求排队。

      response.setStatus(HttpServletResponse.SC_FORBIDDEN);

如果我们发现请求达到了上限的时候,设置相应的状态码,然后前端会根据相应的状态码来完成请求,同时就防止这个ip然后再进行访问。

2. 使用计数器实现限流

计数器限流比较简单,通过记录每个IP的请求次数并在指定时间窗口内进行限流。

限流管理类

创建一个类来管理限流逻辑:

复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@Configuration
@Component
public class CounterRateLimiter {
    private final ConcurrentHashMap<String, AtomicInteger> requestCounts = new ConcurrentHashMap<>();
    private final int maxRequestsPerMinute = 10;

    public boolean tryAcquire(String ipAddress) {
        AtomicInteger requestCount = requestCounts.computeIfAbsent(ipAddress, k -> new AtomicInteger(0));
        int currentCount = requestCount.incrementAndGet();
        log.info("IP: " + ipAddress + ", Current Count: " + currentCount);
        return currentCount <= maxRequestsPerMinute;
    }
    public void resetCounts() {
        log.info("Reset counts");
        requestCounts.clear();
    }

    @Scheduled(fixedRate = 10000)
    public void resetCountsScheduled() {
        log.info("刷新这个ip的次数");
        resetCounts();
    }
}

在开启定时任务的时候,要在Application上添加上@EnableScheduling这个注解

复制代码
@SpringBootApplication
@Configuration
@EnableScheduling
public class Rate1Application {

    public static void main(String[] args) {
        SpringApplication.run(Rate1Application.class, args);
    }

}
控制器中应用限流逻辑

在Spring Boot控制器中应用限流逻辑,并定期重置计数器:

复制代码
private final CounterRateLimiter counterRateLimiter;
    public TestController(CounterRateLimiter counterRateLimiter) {
        this.counterRateLimiter = counterRateLimiter;
    }

    @RequestMapping("/t2")
    public void test2(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String param = request.getRequestURI();
        if (counterRateLimiter.tryAcquire(param)) {
            log.info("打印日志1");
            response.getWriter().write("Request processed for test2 " + param);
        } else {
            log.info("打印日志2");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("Request limit for test2" + param);
        }
    }

@Scheduled(fixedRate = 10000)
public void resetCountsScheduled() {
    log.info("刷新这个ip的次数");
    resetCounts();
}
  • @Scheduled(fixedRate = 10000) :注解用于配置定时任务,每10秒执行一次。
    • fixedRate:设定定时任务的执行频率,这里设置为每10000毫秒(即每10秒)。
  • resetCountsScheduled 方法 :定时任务调用 resetCounts 方法清空计数器。
    • 日志记录:记录定时任务执行。

    • 调用 resetCounts :每10秒自动调用 resetCounts 方法重置计数器。

      public void resetCounts() {
      log.info("Reset counts");
      requestCounts.clear();
      }

resetCounts:用于重置计数器。

  • 日志记录:记录重置计数器操作。
  • clear :清空 requestCounts 中的所有键值对,重置所有IP的计数器
相关推荐
chxii12 分钟前
5java集合框架
java·开发语言
weixin_5450193240 分钟前
微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)
spring boot·微信小程序·uni-app
IsPrisoner1 小时前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang
yychen_java1 小时前
R-tree详解
java·算法·r-tree
JANYI20182 小时前
嵌入式设计模式基础--C语言的继承封装与多态
java·c语言·设计模式
xrkhy2 小时前
反射, 注解, 动态代理
java
Ten peaches2 小时前
Selenium-Java版(操作元素)
java·selenium·测试工具·html
tan180°2 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
一只码代码的章鱼2 小时前
Spring的 @Validate注解详细分析
前端·spring boot·算法
lyw2056192 小时前
RabbitMQ,Kafka八股(自用笔记)
java