51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

神器!SpringBoot 3.3 中实现 API 接口限流就是这么简单

神器!SpringBoot 3.3 中实现 API 接口限流就是这么简单

在互联网飞速发展的今天,随着系统用户规模的不断扩大和分布式架构的广泛应用,API 接口的稳定性和性能成为系统设计中至关重要的因素。无论是应对突发的流量高峰,还是防止恶意爬虫的恶意请求,限流策略都已成为现代系统不可或缺的一部分。

为什么需要接口限流?

  1. 防止系统过载: 在短时间内大量的请求可能导致系统资源耗尽,进而导致服务降级甚至宕机。通过限流,我们可以有效控制流量的上限,确保系统在高负载下仍然能够提供稳定的服务。

  2. 保护关键资源: 一些关键的 API 接口可能涉及到数据库、缓存等有限资源的操作,如果不加限制,可能会导致资源耗尽,影响系统整体性能。限流可以确保这些关键资源的访问量在可控范围内。

  3. 应对恶意攻击: 分布式拒绝服务攻击(DDoS)是常见的网络攻击手段,攻击者通过发送大量请求瘫痪系统。限流策略可以作为第一道防线,快速识别并过滤掉异常流量,减少攻击对系统的影响。

  4. 提升用户体验: 在用户访问量大的情况下,如果不加以控制,可能会出现系统响应速度下降的情况,影响用户体验。合理的限流策略能够为用户提供更加稳定和一致的服务质量。

  5. 公平资源分配: 在多用户、多租户的场景下,限流能够确保系统资源的公平分配,避免某个用户或租户独占资源,影响其他用户的正常使用。

为了解决上述问题,我们可以在 API 接口上实施限流策略,使得系统能够在高并发环境下保持稳定,并且能够合理应对各类突发情况。在本文中,我们将探讨如何在 SpringBoot 3.3 中,通过简单的配置和代码实现 API 接口的限流。

运行效果:

若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。

项目结构

我们将从项目的结构开始,先了解一下本示例项目的文件布局。

rate-limiter/
├── src/
│   ├── main/
│   │   ├── java/com/icoderoad/ratelimiter/
│   │   │   ├── controller/
│   │   │   │   └── RateLimiterController.java
│   │   │   ├── config/
│   │   │   │   └── RateLimiterConfig.java
│   │   │   ├── properties/
│   │   │   │   └── RateLimiterProperties.java
│   │   │   └── application/
│   │   │       └── SpringBootRateLimiterApplication.java
│   │   ├── resources/
│   │   │   ├── templates/
│   │   │   │   └── index.html
│   │   │   └── application.yml
└── pom.xml

接下来,我们将逐步搭建项目,实现 API 接口限流功能。

引入依赖

pom.xml 中引入以下依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>ratelimiter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>ratelimiter</name>
	<description>Demo project for Spring Boot</description>
&lt;properties&gt;
	&lt;java.version&gt;17&lt;/java.version&gt;
	&lt;guava.version&gt;31.1-jre&lt;/guava.version&gt;
&lt;/properties&gt;
&lt;dependencies&gt;
	 &lt;!-- Spring Boot Web Starter --&gt;

        <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-web</artifactId>         </dependency>

        <!-- Guava 用于限流 -->         <dependency>             <groupId>com.google.guava</groupId>             <artifactId>guava</artifactId>             <version>${guava.version}</version>         </dependency>

        <!-- Thymeleaf 模板引擎 -->         <dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-thymeleaf</artifactId>         </dependency>

  <dependency>         <groupId>org.projectlombok</groupId>         <artifactId>lombok</artifactId>         <optional>true</optional>     </dependency>      <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

&lt;build&gt;
	&lt;plugins&gt;
		&lt;plugin&gt;
			&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
			&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
		&lt;/plugin&gt;
	&lt;/plugins&gt;
&lt;/build&gt;

</project>

配置限流参数

src/main/resources/application.yml 中配置限流参数:

server:
  port: 8080

rate-limiter:   permits-per-second: 5       # 每秒许可数   warmup-period: 0            # 预热时间(秒)   timeout: 0                  # 获取许可的超时时间(秒)

参数说明:

  • permits-per-second: 每秒允许的请求数量。

  • warmup-period: 限流器预热时间,用于平滑地增加到最大速率。

  • timeout: 获取许可的超时时间,0 表示立即返回获取结果。

创建限流配置属性类

src/main/java/com/icoderoad/ratelimiter/properties/RateLimiterProperties.java 中创建配置属性类,用于映射 application.yml 中的配置:

package com.icoderoad.ratelimiter.propertie;

import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;

import lombok.Data;

@Data @Component @ConfigurationProperties(prefix = "rate-limiter") public class RateLimiterProperties {

    /**      * 每秒许可数      */     private double permitsPerSecond;

    /**      * 预热时间(秒)      */     private long warmupPeriod;

    /**      * 获取许可的超时时间(秒)      */     private long timeout;

}

说明:

  • 使用 @ConfigurationProperties 注解将配置属性映射到类中,便于在代码中使用。

  • 提供对应的 Getter 和 Setter 方法,方便 Spring Boot 自动注入配置。

配置 RateLimiter

src/main/java/com/icoderoad/ratelimiter/config/RateLimiterConfig.java 中创建限流器配置:

package com.icoderoad.ratelimiter.config;

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;

import com.google.common.util.concurrent.RateLimiter; import com.icoderoad.ratelimiter.propertie.RateLimiterProperties;

@Configuration public class RateLimiterConfig {

    /**      * 配置 RateLimiter Bean      *      * @param properties 注入的限流配置属性      * @return RateLimiter 实例      */     @Bean     public RateLimiter rateLimiter(RateLimiterProperties properties) {         if (properties.getWarmupPeriod() > 0) {             // 创建带有预热期的 RateLimiter             return RateLimiter.create(                     properties.getPermitsPerSecond(),                     properties.getWarmupPeriod(),                     TimeUnit.SECONDS             );         } else {             // 创建标准的 RateLimiter             return RateLimiter.create(properties.getPermitsPerSecond());         }     } }

说明:

  • 根据配置文件中的参数动态创建 RateLimiter 实例。

  • 支持带有预热期的限流器配置,满足不同场景下的需求。

创建控制器

src/main/java/com/icoderoad/ratelimiter/controller/RateLimiterController.java 中创建控制器,处理 API 请求:

package com.icoderoad.ratelimiter.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody;

import com.google.common.util.concurrent.RateLimiter; import com.icoderoad.ratelimiter.propertie.RateLimiterProperties;

@Controller public class RateLimiterController {

    @Autowired     private RateLimiter rateLimiter;

    @Autowired     private RateLimiterProperties properties;

    /**      * 测试限流接口      *      * @return 请求结果      */     @GetMapping("/api/test")     @ResponseBody     public ResponseEntity<String> testApi() {         boolean acquired = rateLimiter.tryAcquire(properties.getTimeout(), TimeUnit.SECONDS);         if (acquired) {             // 允许请求,返回成功响应             return ResponseEntity.ok("请求成功!");         } else {             // 拒绝请求,返回限流响应             return ResponseEntity.status(429).body("请求过多,请稍后再试!");         }     } }

说明:

  • 使用 rateLimiter.tryAcquire(timeout, TimeUnit.SECONDS) 方法尝试获取许可,支持超时等待。

  • 根据获取许可的结果返回对应的响应:

    • 成功获取:返回 200 状态码和成功消息。

    • 获取失败:返回 429 状态码和错误提示。

创建前端页面

src/main/resources/templates/index.html 中创建前端页面,使用 Thymeleaf、Bootstrap 和 jQuery 实现:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>API 限流测试</title>
    <!-- 引入 Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <!-- 自定义样式 -->
    <style>
        body {
            padding-top: 50px;
        }
    </style>
</head>
<body>

<div class="container">     <h1 class="mb-4">API 限流测试</h1>     <button id="testButton" class="btn btn-primary">发送请求</button>     <div id="alertPlaceholder" class="mt-3"></div> </div>

<!-- 引入 jQuery --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <!-- 引入 Bootstrap JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

<script>     $(document).ready(function () {         $('#testButton').click(function () {             $.ajax({                 url: '/api/test',                 method: 'GET',                 success: function (response) {                     showAlert(response, 'success');                 },                 error: function (xhr) {                     if (xhr.status === 429) {                         showAlert(xhr.responseText, 'danger');                     } else {                         showAlert('发生未知错误,请稍后重试。', 'warning');                     }                 }             });         });

        /**          * 显示提示信息          * @param message 消息内容          * @param type 提示类型('success', 'danger', 'warning' 等)          */         function showAlert(message, type) {             const alertHtml =                 &lt;div class=&quot;alert alert-${type} alert-dismissible fade show&quot; role=&quot;alert&quot;&gt;                     ${message}                     &lt;button type=&quot;button&quot; class=&quot;btn-close&quot; data-bs-dismiss=&quot;alert&quot; aria-label=&quot;Close&quot;&gt;&lt;/button&gt;                 &lt;/div&gt;             ;             $('#alertPlaceholder').html(alertHtml);         }     }); </script>

</body> </html>

说明:

  • 引入资源:

    • 使用 CDN 加载 Bootstrap 和 jQuery,确保资源的快速和稳定加载。
  • 页面结构:

    • 一个按钮用于触发 API 请求。

    • 一个占位符 div 用于显示提示信息。

  • JavaScript 逻辑:

    • 使用 jQuery 监听按钮点击事件,发送 AJAX 请求到 /api/test 接口。

    • 根据响应结果,调用 showAlert 函数,在页面上显示不同类型的提示信息。

    • showAlert 函数使用 Bootstrap 的 Alert 组件,提供友好的用户提示。

效果展示:

  • 请求成功: 显示绿色的成功提示。

  • 请求被限流: 显示红色的错误提示,提示用户请求过多。

  • 未知错误: 显示黄色的警告提示,提示发生未知错误。

启动应用

src/main/java/com/icoderoad/ratelimiter/application/SpringBootRateLimiterApplication.java 中启动 Spring Boot 应用:

package com.icoderoad.ratelimiter;

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.icoderoad.ratelimiter") public class RatelimiterApplication {

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

}

说明:

  • 使用 @SpringBootApplication 注解标注主启动类,并指定扫描的基础包路径。

  • 运行 main 方法即可启动应用。

8. 测试与验证

步骤:

  1. 启动应用: 运行主启动类,启动 Spring Boot 应用。

  2. 访问页面: 在浏览器中访问 http://localhost:8080/,看到 API 限流测试页面。

  3. 发送请求:

    点击"发送请求"按钮,观察页面提示信息。

    • 正常情况: 如果请求未超过限流阈值,显示绿色的"请求成功!"提示。

    • 限流情况: 如果在短时间内连续多次点击按钮,超过配置的每秒许可数,将显示红色的"请求过多,请稍后再试!"提示。

  4. 调整配置: 可以修改 application.yml 中的限流参数,重新启动应用,测试不同的限流策略效果。

示例演示:

  • 设置 permits-per-second 为 2,表示每秒允许 2 个请求。

  • 连续快速点击按钮,多数请求将被限流,提示用户稍后重试。

9. 总结

通过本文的示例,我们成功地在 Spring Boot 3.3 中实现了简单而有效的 API 接口限流功能。我们利用了 Guava 提供的 RateLimiter 工具,结合 Spring Boot 的配置属性管理和依赖注入机制,实现了灵活可配的限流策略。同时,通过前端页面的简单设计和友好提示,使得用户能够清晰地感知到限流机制的存在和作用。

赞(5)
未经允许不得转载:工具盒子 » 神器!SpringBoot 3.3 中实现 API 接口限流就是这么简单