【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的计数器
相关推荐
陌殇殇14 小时前
001 Spring AI Alibaba框架整合百炼大模型平台 — 快速入门
人工智能·spring boot·ai
言慢行善15 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星15 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟15 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z15 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可15 小时前
Java 中的实现类是什么
java·开发语言
He少年15 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新15 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏49416 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构