在Spring Boot中实现接口防抖(即防止接口在短时间内被频繁调用)

由于项目中未使用到redis,本次处理使用Guava Cache来实现接口防抖

引入依赖

<dependency>  
    <groupId>com.google.guava</groupId>  
    <artifactId>guava</artifactId>  
    <version>30.1-jre</version> <!-- 请检查并使用最新版本 -->  
</dependency>

创建防抖过滤器(示例代码)

package com.sqx.config;

import com.alibaba.fastjson.JSON;
import com.sqx.common.utils.IPUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

@Component
public class AntiShakeFilter extends OncePerRequestFilter {

    private final Cache<String, Boolean> requestCache;
	//在Guava Cache中,.expireAfterWrite(5, TimeUnit.SECONDS) 设置的是写入后过期时间,这意味着缓存项在写入缓存后会在指定的时间段(这里设置的是3秒)后自动过期
    public AntiShakeFilter() {
        requestCache = CacheBuilder.newBuilder()
                .expireAfterWrite(3, TimeUnit.SECONDS) // 设置防抖时间为5秒
                .maximumSize(1000) // 设置缓存的最大容量  
                .build();
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // 假设你从请求中提取出一个可以用来标识请求唯一性的key,比如用户ID和请求路径的组合  
        String key = extractUniqueKey(request);

        // 检查缓存中是否存在这个key  
        if (requestCache.asMap().containsKey(key)) {
            // 如果存在,说明是防抖时间窗口内的重复请求,可以选择记录日志、返回特定响应等  
            // 这里简单示例,只是记录日志并结束过滤链  
            // 注意:在实际应用中,你可能需要发送一个特定的响应给客户端,而不是继续处理请求  
            // 例如,可以设置一个HTTP 429状态码(Too Many Requests)  
            // ...
             // 终止处理,不继续向下传递请求
            // 发送一个HTTP 429状态码(Too Many Requests)
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

            // 设置响应内容类型(可选)
            response.setContentType("application/json;charset=UTF-8");
            Map<String,Object> resultMap = new HashMap<>();
            resultMap.put("code",429);
            resultMap.put("msg","请求过快~");
            // 写入响应体,例如JSON格式的错误消息
            try (PrintWriter out = response.getWriter()) {
                // 将Map转换为JSON字符串并写入响应体
                String jsonString = JSON.toJSONString(resultMap);
                out.println(jsonString);
            } catch (IOException e) {
                // 处理写入异常
                e.printStackTrace();
            }

            // 由于已经发送了响应,所以不需要继续处理过滤器链
            return; // 终止处理,不继续向下传递请求
        }

        // 如果缓存中不存在这个key,将key加入缓存,并继续处理请求  
        requestCache.put(key, true); // 这里的值通常是无关紧要的,因为我们只关心key是否存在  

        // 继续处理请求(通过调用filterChain.doFilter())  
        filterChain.doFilter(request, response);
    }

    // 辅助方法,用于从请求中提取唯一键  
    private String extractUniqueKey(HttpServletRequest request) {
        String ipAddr = IPUtils.getIpAddr(request);
        String requestURI = request.getRequestURI();
        // 根据你的应用逻辑提取唯一键,例如:userId + requestURI  
        // ...  
        return ipAddr+":"+requestURI;
    }
}

在这个示例中,AntiShakeFilter被配置为一个Spring组件(使用@Component注解),并且实现了OncePerRequestFilter。在doFilterInternal方法中,我们首先从请求中提取出一个唯一键(key),然后检查这个键是否存在于Guava Cache中。如果存在,说明是防抖时间窗口内的重复请求,你可以选择记录日志、发送特定的HTTP响应等。如果不存在,则将键加入缓存,并继续处理请求(通过调用filterChain.doFilter())。

请注意,你需要根据你的应用逻辑来实现extractUniqueKey方法,以便能够准确地提取出用于标识请求唯一性的键。这个键应该足够唯一,以便能够区分不同的请求。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值