SpringBoot接口防抖(防重复提交)的一些实现方案

本文探讨了接口防抖的概念和重要性,特别是在用户输入、按钮点击和滚动加载场景。介绍了如何确定重复接口请求,并在分布式部署下通过共享缓存和分布式锁实现接口防抖。提供了一个具体的实现方案,包括请求锁、唯一key生成和重复提交判断,展示了使用Redis和Redisson的示例代码。
摘要由CSDN通过智能技术生成
  • 前言

  • 啥是防抖

  • 思路解析

  • 分布式部署下如何做接口防抖?

  • 具体实现

  • 请求锁

  • 唯一key生成

  • 重复提交判断


前言

作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。

啥是防抖

图片

图片

所谓防抖,一是防用户手抖,二是防网络抖动。在Web系统中,表单提交是一个非常常见的功能,如果不加控制,容易因为用户的误操作或网络延迟导致同一请求被发送多次,进而生成重复的数据记录。要针对用户的误操作,前端通常会实现按钮的loading状态,阻止用户进行多次点击。而对于网络波动造成的请求重发问题,仅靠前端是不行的。为此,后端也应实施相应的防抖逻辑,确保在网络波动的情况下不会接收并处理同一请求多次。

一个理想的防抖组件或机制,我觉得应该具备以下特点:

  1. 逻辑正确,也就是不能误判;

  2. 响应迅速,不能太慢;

  3. 易于集成,逻辑与业务解耦;

  4. 良好的用户反馈机制,比如提示“您点击的太快了”

思路解析

前面讲了那么多,我们已经知道接口的防抖是很有必要的了,但是在开发之前,我们需要捋清楚几个问题。

哪一类接口需要防抖?

接口防抖也不是每个接口都需要加,一般需要加防抖的接口有这几类:

  • 用户输入类接口:比如搜索框输入、表单输入等,用户输入往往会频繁触发接口请求,但是每次触发并不一定需要立即发送请求,可以等待用户完成输入一段时间后再发送请求。

  • 按钮点击类接口:比如提交表单、保存设置等,用户可能会频繁点击按钮,但是每次点击并不一定需要立即发送请求,可以等待用户停止点击一段时间后再发送请求。

  • 滚动加载类接口:比如下拉刷新、上拉加载更多等,用户可能在滚动过程中频繁触发接口请求,但是每次触发并不一定需要立即发送请求,可以等待用户停止滚动一段时间后再发送请求。

如何确定接口是重复的?

防抖也即防重复提交,那么如何确定两次接口就是重复的呢?首先,我们需要给这两次接口的调用加一个时间间隔,大于这个时间间隔的一定不是重复提交;其次,两次请求提交的参数比对,不一定要全部参数,选择标识性强的参数即可;最后,如果想做的更好一点,还可以加一个请求地址的对比。

分布式部署下如何做接口防抖?

有两个方案:

使用共享缓存

流程图如下:

图片

图片

使用分布式锁

流程图如下:

图片

图片

常见的分布式组件有Redis、Zookeeper等,但结合实际业务来看,一般都会选择Redis,因为Redis一般都是Web系统必备的组件,不需要额外搭建。

具体实现

现在有一个保存用户的接口

@PostMapping("/add")
@RequiresPermissions(value = "add")
@Log(methodDesc = "添加用户")
public ResponseEntity<String> add(@RequestBody AddReq addReq) {
        return userService.add(addReq);
}

AddReq.java

package com.summo.demo.model.request;
import java.util.List;
import lombok.Data;
@
Spring Boot 中的接口防抖(Throttle or Debounce)通常是指为了限制频繁调用某个接口而采取的一种策略,它会在用户连续多次请求之间设置一段延迟,只有当这个时间间隔超过预设值后,才会允许再次发送请求。这在处理高并发、止滥用或服务降级时很有用。 Spring Boot 并没有直接提供这样的功能,但是可以结合第三方库如 Spring Cloud Gateway 或者自定义过滤器(Filter)来实现。你可以利用滑动窗口算法(Sliding Window Algorithm)或者令牌桶算法(Token Bucket)来控制接口访问频率。例如,可以使用 `RateLimiter` 或者 Redis 的过期键来管理访问令牌。 以下是简单的一个示例,使用 Spring Cloud Gateway 的 Filter: ```java import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; import reactor.core.publisher.Mono; public class CustomKeyResolver implements KeyResolver { private final RateLimiter rateLimiter; public CustomKeyResolver(RateLimiter rateLimiter) { this.rateLimiter = rateLimiter; } @Override public Mono<String> resolve(Object exchange) { // 这里可以根据实际情况提取请求关键信息作为 key String apiKey = extractApiKeyFromExchange(exchange); return Mono.just(apiKey).doOnNext(key -> { if (!rateLimiter.tryAcquire()) { // 如果没有获取到令牌,则记录防抖 log.info("Debouncing API call for key {}", apiKey); } }); } private String extractApiKeyFromExchange(Object exchange) { // 实现从exchange中提取API密钥的逻辑 } } // 在GlobalFilter配置中启用 @Bean public GlobalFilter customRateLimitingFilter(GatewayFilterChain chain, RateLimiter rateLimiter) { return new ConditionalGlobalFilter( (exchange) -> true, // 条件总是true,表示始终开启限流 new CustomKeyResolver(rateLimiter), chain ); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值